|
5 | 5 | package archive
|
6 | 6 |
|
7 | 7 | import (
|
8 |
| - "archive/tar" |
9 | 8 | "context"
|
10 | 9 | "io"
|
11 |
| - "os" |
12 |
| - "os/exec" |
13 |
| - "path" |
14 |
| - "sort" |
15 |
| - "syscall" |
16 | 10 | "time"
|
17 | 11 |
|
18 |
| - "github.com/moby/moby/pkg/system" |
| 12 | + "github.com/containers/storage/pkg/archive" |
| 13 | + "github.com/containers/storage/pkg/idtools" |
19 | 14 | "github.com/opentracing/opentracing-go"
|
20 |
| - "golang.org/x/xerrors" |
21 | 15 |
|
22 | 16 | "github.com/gitpod-io/gitpod/common-go/log"
|
23 | 17 | "github.com/gitpod-io/gitpod/common-go/tracing"
|
@@ -66,141 +60,29 @@ func ExtractTarbal(ctx context.Context, src io.Reader, dst string, opts ...TarOp
|
66 | 60 | opt(&cfg)
|
67 | 61 | }
|
68 | 62 |
|
69 |
| - pr, pw := io.Pipe() |
70 |
| - src = io.TeeReader(src, pw) |
71 |
| - tarReader := tar.NewReader(pr) |
72 |
| - |
73 |
| - type Info struct { |
74 |
| - UID, GID int |
75 |
| - IsSymlink bool |
76 |
| - Xattrs map[string]string |
77 |
| - } |
78 |
| - |
79 |
| - finished := make(chan bool) |
80 |
| - m := make(map[string]Info) |
81 |
| - |
82 |
| - go func() { |
83 |
| - defer close(finished) |
84 |
| - for { |
85 |
| - hdr, err := tarReader.Next() |
86 |
| - if err == io.EOF { |
87 |
| - finished <- true |
88 |
| - return |
89 |
| - } |
90 |
| - |
91 |
| - if err != nil { |
92 |
| - log.WithError(err).Error("error reading tar") |
93 |
| - return |
94 |
| - } |
95 |
| - |
96 |
| - m[hdr.Name] = Info{ |
97 |
| - UID: hdr.Uid, |
98 |
| - GID: hdr.Gid, |
99 |
| - IsSymlink: (hdr.Linkname != ""), |
100 |
| - //nolint:staticcheck |
101 |
| - Xattrs: hdr.Xattrs, |
102 |
| - } |
| 63 | + uidMaps := make([]idtools.IDMap, len(cfg.UIDMaps)) |
| 64 | + for i, m := range cfg.UIDMaps { |
| 65 | + uidMaps[i] = idtools.IDMap{ |
| 66 | + ContainerID: m.ContainerID, |
| 67 | + HostID: m.HostID, |
| 68 | + Size: m.Size, |
103 | 69 | }
|
104 |
| - }() |
105 |
| - |
106 |
| - // Be explicit about the tar flags. We want to restore the exact content without changes |
107 |
| - tarcmd := exec.Command( |
108 |
| - "tar", |
109 |
| - "--extract", |
110 |
| - "--preserve-permissions", |
111 |
| - "--xattrs", "--xattrs-include=security.capability", |
112 |
| - ) |
113 |
| - tarcmd.Dir = dst |
114 |
| - tarcmd.Stdin = src |
115 |
| - |
116 |
| - var msg []byte |
117 |
| - msg, err = tarcmd.CombinedOutput() |
118 |
| - if err != nil { |
119 |
| - return xerrors.Errorf("tar %s: %s", dst, err.Error()+";"+string(msg)) |
120 | 70 | }
|
121 |
| - |
122 |
| - log.WithField("log", string(msg)).Debug("decompressing tar stream log") |
123 |
| - |
124 |
| - <-finished |
125 |
| - |
126 |
| - // lets create a sorted list of pathes and chown depth first. |
127 |
| - paths := make([]string, 0, len(m)) |
128 |
| - for path := range m { |
129 |
| - paths = append(paths, path) |
130 |
| - } |
131 |
| - sort.Sort(sort.Reverse(sort.StringSlice(paths))) |
132 |
| - |
133 |
| - // We need to remap the UID and GID between the host and the container to avoid permission issues. |
134 |
| - for _, p := range paths { |
135 |
| - v := m[p] |
136 |
| - uid := toHostID(v.UID, cfg.UIDMaps) |
137 |
| - gid := toHostID(v.GID, cfg.GIDMaps) |
138 |
| - |
139 |
| - if v.IsSymlink { |
140 |
| - continue |
141 |
| - } |
142 |
| - |
143 |
| - err = remapFile(path.Join(dst, p), uid, gid, v.Xattrs) |
144 |
| - if err != nil { |
145 |
| - log.WithError(err).WithField("uid", uid).WithField("gid", gid).WithField("path", p).Warn("cannot chown") |
| 71 | + gidMaps := make([]idtools.IDMap, len(cfg.GIDMaps)) |
| 72 | + for i, m := range cfg.GIDMaps { |
| 73 | + gidMaps[i] = idtools.IDMap{ |
| 74 | + ContainerID: m.ContainerID, |
| 75 | + HostID: m.HostID, |
| 76 | + Size: m.Size, |
146 | 77 | }
|
147 | 78 | }
|
148 | 79 |
|
149 |
| - log.WithField("duration", time.Since(start).Milliseconds()).Debug("untar complete") |
150 |
| - return nil |
151 |
| -} |
| 80 | + err = archive.Untar(src, dst, &archive.TarOptions{ |
| 81 | + UIDMaps: uidMaps, |
| 82 | + GIDMaps: gidMaps, |
| 83 | + Compression: archive.Uncompressed, |
| 84 | + }) |
152 | 85 |
|
153 |
| -func toHostID(containerID int, idMap []IDMapping) int { |
154 |
| - for _, m := range idMap { |
155 |
| - if (containerID >= m.ContainerID) && (containerID <= (m.ContainerID + m.Size - 1)) { |
156 |
| - hostID := m.HostID + (containerID - m.ContainerID) |
157 |
| - return hostID |
158 |
| - } |
159 |
| - } |
160 |
| - return containerID |
161 |
| -} |
162 |
| - |
163 |
| -// remapFile changes the UID and GID of a file preserving existing file mode bits. |
164 |
| -func remapFile(name string, uid, gid int, xattrs map[string]string) error { |
165 |
| - // current info of the file before any change |
166 |
| - fileInfo, err := os.Stat(name) |
167 |
| - if err != nil { |
168 |
| - return err |
169 |
| - } |
170 |
| - |
171 |
| - // nothing to do for symlinks |
172 |
| - if fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink { |
173 |
| - return nil |
174 |
| - } |
175 |
| - |
176 |
| - // changing UID or GID can break files with suid/sgid |
177 |
| - err = os.Lchown(name, uid, gid) |
178 |
| - if err != nil { |
179 |
| - return err |
180 |
| - } |
181 |
| - |
182 |
| - // restore original permissions |
183 |
| - err = os.Chmod(name, fileInfo.Mode()) |
184 |
| - if err != nil { |
185 |
| - return err |
186 |
| - } |
187 |
| - |
188 |
| - for key, value := range xattrs { |
189 |
| - if err := system.Lsetxattr(name, key, []byte(value), 0); err != nil { |
190 |
| - log.WithField("name", key).WithField("value", value).WithField("file", name).WithError(err).Error("restoring extended attributes") |
191 |
| - if err == syscall.ENOTSUP || err == syscall.EPERM { |
192 |
| - continue |
193 |
| - } |
194 |
| - |
195 |
| - return err |
196 |
| - } |
197 |
| - } |
198 |
| - |
199 |
| - // restore file times |
200 |
| - fileTime := fileInfo.Sys().(*syscall.Stat_t) |
201 |
| - return os.Chtimes(name, timespecToTime(fileTime.Atim), timespecToTime(fileTime.Mtim)) |
202 |
| -} |
203 |
| - |
204 |
| -func timespecToTime(ts syscall.Timespec) time.Time { |
205 |
| - return time.Unix(int64(ts.Sec), int64(ts.Nsec)) |
| 86 | + log.WithField("duration", time.Since(start).Milliseconds()).Debug("untar complete") |
| 87 | + return |
206 | 88 | }
|
0 commit comments