(entry archiveEntry)
| 171 | } |
| 172 | |
| 173 | func (a *ArchiveBuilder) writeEntry(entry archiveEntry) error { |
| 174 | pathInTar := entry.path |
| 175 | header := entry.header |
| 176 | |
| 177 | if header.Typeflag != tar.TypeReg { |
| 178 | // anything other than a regular file (e.g. dir, symlink) just needs the header |
| 179 | if err := a.tw.WriteHeader(header); err != nil { |
| 180 | return fmt.Errorf("writing %q header: %w", pathInTar, err) |
| 181 | } |
| 182 | return nil |
| 183 | } |
| 184 | |
| 185 | file, err := os.Open(pathInTar) |
| 186 | if err != nil { |
| 187 | // In case the file has been deleted since we last looked at it. |
| 188 | if os.IsNotExist(err) { |
| 189 | return nil |
| 190 | } |
| 191 | return err |
| 192 | } |
| 193 | |
| 194 | defer func() { |
| 195 | _ = file.Close() |
| 196 | }() |
| 197 | |
| 198 | // The size header must match the number of contents bytes. |
| 199 | // |
| 200 | // There is room for a race condition here if something writes to the file |
| 201 | // after we've read the file size. |
| 202 | // |
| 203 | // For small files, we avoid this by first copying the file into a buffer, |
| 204 | // and using the size of the buffer to populate the header. |
| 205 | // |
| 206 | // For larger files, we don't want to copy the whole thing into a buffer, |
| 207 | // because that would blow up heap size. There is some danger that this |
| 208 | // will lead to a spurious error when the tar writer validates the sizes. |
| 209 | // That error will be disruptive but will be handled as best as we |
| 210 | // can downstream. |
| 211 | useBuf := header.Size < 5000000 |
| 212 | if useBuf { |
| 213 | a.copyBuf.Reset() |
| 214 | _, err = io.Copy(a.copyBuf, file) |
| 215 | if err != nil && !errors.Is(err, io.EOF) { |
| 216 | return fmt.Errorf("copying %q: %w", pathInTar, err) |
| 217 | } |
| 218 | header.Size = int64(len(a.copyBuf.Bytes())) |
| 219 | } |
| 220 | |
| 221 | // wait to write the header until _after_ the file is successfully opened |
| 222 | // to avoid generating an invalid tar entry that has a header but no contents |
| 223 | // in the case the file has been deleted |
| 224 | err = a.tw.WriteHeader(header) |
| 225 | if err != nil { |
| 226 | return fmt.Errorf("writing %q header: %w", pathInTar, err) |
| 227 | } |
| 228 | |
| 229 | if useBuf { |
| 230 | _, err = io.Copy(a.tw, a.copyBuf) |
no test coverage detected