|
1 | 1 | package workspace |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "archive/tar" |
4 | 5 | "bytes" |
5 | 6 | "context" |
6 | 7 | "crypto/rand" |
7 | 8 | "encoding/hex" |
8 | 9 | "fmt" |
| 10 | + "io" |
9 | 11 | "os" |
| 12 | + "path" |
10 | 13 | "sort" |
11 | | - "strings" |
12 | 14 |
|
13 | 15 | "github.com/sourcegraph/sourcegraph/lib/errors" |
14 | 16 |
|
@@ -178,41 +180,97 @@ func (wc *dockerVolumeWorkspaceCreator) copyFilesIntoVolumes(ctx context.Context |
178 | 180 | return nil |
179 | 181 | } |
180 | 182 |
|
| 183 | + archive, err := wc.archiveAdditionalFiles(files) |
| 184 | + if err != nil { |
| 185 | + return err |
| 186 | + } |
| 187 | + defer os.Remove(archive) |
| 188 | + |
181 | 189 | opts := append([]string{ |
182 | 190 | "run", |
183 | 191 | "--rm", |
184 | 192 | "--init", |
185 | 193 | "--workdir", "/work", |
| 194 | + "--mount", "type=bind,source=" + archive + ",target=/tmp/additional-files.tar,ro", |
186 | 195 | }, w.dockerRunOptsWithUser(w.uidGid, "/work")...) |
187 | 196 |
|
188 | | - // We sort these so our tests don't break. Sorry. |
| 197 | + opts = append( |
| 198 | + opts, |
| 199 | + DockerVolumeWorkspaceImage, |
| 200 | + "tar", "-xf", "/tmp/additional-files.tar", "-C", "/work", |
| 201 | + ) |
| 202 | + |
| 203 | + if out, err := exec.CommandContext(ctx, "docker", opts...).CombinedOutput(); err != nil { |
| 204 | + return errors.Wrapf(err, "additional files output:\n\n%s\n\n", string(out)) |
| 205 | + } |
| 206 | + return nil |
| 207 | +} |
| 208 | + |
| 209 | +func (wc *dockerVolumeWorkspaceCreator) archiveAdditionalFiles(files map[string]string) (archivePath string, err error) { |
| 210 | + f, err := os.CreateTemp(wc.tempDir, "src-additional-files-*.tar") |
| 211 | + if err != nil { |
| 212 | + return "", errors.Wrap(err, "creating additional files archive") |
| 213 | + } |
| 214 | + archivePath = f.Name() |
| 215 | + defer func() { |
| 216 | + if err != nil { |
| 217 | + f.Close() |
| 218 | + os.Remove(archivePath) |
| 219 | + } |
| 220 | + }() |
| 221 | + |
| 222 | + tw := tar.NewWriter(f) |
189 | 223 | var names []string |
190 | 224 | for name := range files { |
191 | 225 | names = append(names, name) |
192 | 226 | } |
193 | 227 | sort.Strings(names) |
194 | 228 |
|
195 | | - var copyCmds []string |
196 | 229 | for _, name := range names { |
197 | | - localPath := files[name] |
198 | | - opts = append(opts, []string{ |
199 | | - "--mount", "type=bind,source=" + localPath + ",target=/tmp/" + name + ",ro", |
200 | | - }...) |
| 230 | + if name == "" || path.IsAbs(name) || path.Clean(name) != name || name == ".." || len(name) >= 3 && name[:3] == "../" { |
| 231 | + return "", errors.Errorf("invalid additional file path %q", name) |
| 232 | + } |
201 | 233 |
|
202 | | - copyCmds = append(copyCmds, "cp /tmp/"+name+" /work/"+name) |
203 | | - } |
| 234 | + file, err := os.Open(files[name]) |
| 235 | + if err != nil { |
| 236 | + return "", errors.Wrapf(err, "opening additional file %q", name) |
| 237 | + } |
| 238 | + info, err := file.Stat() |
| 239 | + if err != nil { |
| 240 | + file.Close() |
| 241 | + return "", errors.Wrapf(err, "stating additional file %q", name) |
| 242 | + } |
| 243 | + if !info.Mode().IsRegular() { |
| 244 | + file.Close() |
| 245 | + return "", errors.Errorf("additional file %q is not a regular file", name) |
| 246 | + } |
204 | 247 |
|
205 | | - opts = append( |
206 | | - opts, |
207 | | - DockerVolumeWorkspaceImage, |
208 | | - "sh", "-c", |
209 | | - strings.Join(copyCmds, " && ")+";", |
210 | | - ) |
| 248 | + header, err := tar.FileInfoHeader(info, "") |
| 249 | + if err != nil { |
| 250 | + file.Close() |
| 251 | + return "", errors.Wrapf(err, "creating archive header for additional file %q", name) |
| 252 | + } |
| 253 | + header.Name = name |
| 254 | + if err := tw.WriteHeader(header); err != nil { |
| 255 | + file.Close() |
| 256 | + return "", errors.Wrapf(err, "writing archive header for additional file %q", name) |
| 257 | + } |
| 258 | + if _, err := io.Copy(tw, file); err != nil { |
| 259 | + file.Close() |
| 260 | + return "", errors.Wrapf(err, "archiving additional file %q", name) |
| 261 | + } |
| 262 | + if err := file.Close(); err != nil { |
| 263 | + return "", errors.Wrapf(err, "closing additional file %q", name) |
| 264 | + } |
| 265 | + } |
211 | 266 |
|
212 | | - if out, err := exec.CommandContext(ctx, "docker", opts...).CombinedOutput(); err != nil { |
213 | | - return errors.Wrapf(err, "unzip output:\n\n%s\n\n", string(out)) |
| 267 | + if err := tw.Close(); err != nil { |
| 268 | + return "", errors.Wrap(err, "closing additional files archive") |
214 | 269 | } |
215 | | - return nil |
| 270 | + if err := f.Close(); err != nil { |
| 271 | + return "", errors.Wrap(err, "closing additional files archive file") |
| 272 | + } |
| 273 | + return archivePath, nil |
216 | 274 | } |
217 | 275 |
|
218 | 276 | // dockerVolumeWorkspace workspaces are placed on Docker volumes (surprise!), |
|
0 commit comments