// writeStdioSymlinks adds the /dev/stdin, /dev/stdout, /dev/stderr, and // /dev/fd symlinks expected by Docker to the converted ACIs so apps can find // them as expected func writeStdioSymlinks(tarWriter *tar.Writer, fileMap map[string]struct{}, pwl []string) ([]string, error) { stdioSymlinks := map[string]string{ "/dev/stdin": "/proc/self/fd/0", // Docker makes /dev/{stdout,stderr} point to /proc/self/fd/{1,2} but // we point to /dev/console instead in order to support the case when // stdout/stderr is a Unix socket (e.g. for the journal). "/dev/stdout": "/dev/console", "/dev/stderr": "/dev/console", "/dev/fd": "/proc/self/fd", } for name, target := range stdioSymlinks { if _, exists := fileMap[name]; exists { continue } hdr := &tar.Header{ Name: filepath.Join("rootfs", name), Mode: 0777, Typeflag: tar.TypeSymlink, Linkname: target, } if err := tarWriter.WriteHeader(hdr); err != nil { return nil, err } if !util.In(pwl, name) { pwl = append(pwl, name) } } return pwl, nil }
func writeACI(layer io.ReadSeeker, manifest schema.ImageManifest, curPwl []string, output string, compress bool) (*schema.ImageManifest, error) { aciFile, err := os.Create(output) if err != nil { return nil, fmt.Errorf("error creating ACI file: %v", err) } defer aciFile.Close() var w io.WriteCloser = aciFile if compress { w = gzip.NewWriter(aciFile) defer w.Close() } trw := tar.NewWriter(w) defer trw.Close() if err := WriteRootfsDir(trw); err != nil { return nil, fmt.Errorf("error writing rootfs entry: %v", err) } var whiteouts []string convWalker := func(t *tarball.TarFile) error { name := t.Name() if name == "./" { return nil } t.Header.Name = path.Join("rootfs", name) absolutePath := strings.TrimPrefix(t.Header.Name, "rootfs") if strings.Contains(t.Header.Name, "/.wh.") { whiteouts = append(whiteouts, strings.Replace(absolutePath, ".wh.", "", 1)) return nil } if t.Header.Typeflag == tar.TypeLink { t.Header.Linkname = path.Join("rootfs", t.Linkname()) } if err := trw.WriteHeader(t.Header); err != nil { return err } if _, err := io.Copy(trw, t.TarStream); err != nil { return err } if !util.In(curPwl, absolutePath) { curPwl = append(curPwl, absolutePath) } return nil } reader, err := aci.NewCompressedTarReader(layer) if err == nil { // write files in rootfs/ if err := tarball.Walk(*reader, convWalker); err != nil { return nil, err } } else { // ignore errors: empty layers in tars generated by docker save are not // valid tar files so we ignore errors trying to open them. Converted // ACIs will have the manifest and an empty rootfs directory in any // case. } newPwl := subtractWhiteouts(curPwl, whiteouts) manifest.PathWhitelist = newPwl if err := WriteManifest(trw, manifest); err != nil { return nil, fmt.Errorf("error writing manifest: %v", err) } return &manifest, nil }