(r io.Reader, dir string)
| 98 | } |
| 99 | |
| 100 | func untar(r io.Reader, dir string) (err error) { |
| 101 | t0 := time.Now() |
| 102 | nFiles := 0 |
| 103 | madeDir := map[string]bool{} |
| 104 | |
| 105 | zr, err := gzip.NewReader(r) |
| 106 | if err != nil { |
| 107 | return fmt.Errorf("requires gzip-compressed body: %v", err) |
| 108 | } |
| 109 | |
| 110 | tr := tar.NewReader(zr) |
| 111 | for { |
| 112 | f, err := tr.Next() |
| 113 | if err == io.EOF { |
| 114 | break |
| 115 | } |
| 116 | if err != nil { |
| 117 | return fmt.Errorf("tar error: %v", err) |
| 118 | } |
| 119 | if !validRelPath(f.Name) { |
| 120 | return fmt.Errorf("tar contained invalid name error %q", f.Name) |
| 121 | } |
| 122 | rel := filepath.FromSlash(f.Name) |
| 123 | abs := filepath.Join(dir, rel) |
| 124 | |
| 125 | fi := f.FileInfo() |
| 126 | mode := fi.Mode() |
| 127 | switch { |
| 128 | case mode.IsRegular(): |
| 129 | dir := filepath.Dir(abs) |
| 130 | if !madeDir[dir] { |
| 131 | if err := os.MkdirAll(filepath.Dir(abs), 0755); err != nil { |
| 132 | return err |
| 133 | } |
| 134 | madeDir[dir] = true |
| 135 | } |
| 136 | wf, err := os.OpenFile(abs, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode.Perm()) |
| 137 | if err != nil { |
| 138 | return err |
| 139 | } |
| 140 | n, err := io.Copy(wf, tr) |
| 141 | if closeErr := wf.Close(); closeErr != nil && err == nil { |
| 142 | err = closeErr |
| 143 | } |
| 144 | if err != nil { |
| 145 | return fmt.Errorf("error writing to %s: %v", abs, err) |
| 146 | } |
| 147 | if n != f.Size { |
| 148 | return fmt.Errorf("only wrote %d bytes to %s; expected %d", n, abs, f.Size) |
| 149 | } |
| 150 | modTime := f.ModTime |
| 151 | if modTime.After(t0) { |
| 152 | modTime = t0 |
| 153 | } |
| 154 | if !modTime.IsZero() { |
| 155 | _ = os.Chtimes(abs, modTime, modTime) |
| 156 | } |
| 157 | nFiles++ |
no test coverage detected