b0d669c05b
When we use git hash-object --stdin with the --path option, Git applies filters to the object, so Git LFS is invoked. However, if the object provided is less than 1024 bytes in size, we would hang. This occurred because of our packet reader didn't quite implement the io.Reader interface completely: if it returned a non-zero value and io.EOF, the next call to Read would not return 0 and io.EOF. Instead, it would try to read from stdin, which would not be sending us more data until we provided a response, so we would hang. To solve this, keep track of the EOF and always return it on subsequent Read calls. In addition, don't process the callback to write the file in this case, since we don't actually want to write into the working tree.
108 lines
2.3 KiB
Go
108 lines
2.3 KiB
Go
package lfs
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/git-lfs/git-lfs/errors"
|
|
"github.com/git-lfs/git-lfs/tools"
|
|
)
|
|
|
|
type cleanedAsset struct {
|
|
Filename string
|
|
*Pointer
|
|
}
|
|
|
|
func (f *GitFilter) Clean(reader io.Reader, fileName string, fileSize int64, cb tools.CopyCallback) (*cleanedAsset, error) {
|
|
extensions, err := f.cfg.SortedExtensions()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var oid string
|
|
var size int64
|
|
var tmp *os.File
|
|
var exts []*PointerExtension
|
|
if len(extensions) > 0 {
|
|
request := &pipeRequest{"clean", reader, fileName, extensions}
|
|
|
|
var response pipeResponse
|
|
if response, err = pipeExtensions(f.cfg, request); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
oid = response.results[len(response.results)-1].oidOut
|
|
tmp = response.file
|
|
var stat os.FileInfo
|
|
if stat, err = os.Stat(tmp.Name()); err != nil {
|
|
return nil, err
|
|
}
|
|
size = stat.Size()
|
|
|
|
for _, result := range response.results {
|
|
if result.oidIn != result.oidOut {
|
|
ext := NewPointerExtension(result.name, len(exts), result.oidIn)
|
|
exts = append(exts, ext)
|
|
}
|
|
}
|
|
} else {
|
|
oid, size, tmp, err = f.copyToTemp(reader, fileSize, cb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
pointer := NewPointer(oid, size, exts)
|
|
return &cleanedAsset{tmp.Name(), pointer}, err
|
|
}
|
|
|
|
func (f *GitFilter) copyToTemp(reader io.Reader, fileSize int64, cb tools.CopyCallback) (oid string, size int64, tmp *os.File, err error) {
|
|
tmp, err = TempFile(f.cfg, "")
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
defer tmp.Close()
|
|
|
|
oidHash := sha256.New()
|
|
writer := io.MultiWriter(oidHash, tmp)
|
|
|
|
if fileSize <= 0 {
|
|
cb = nil
|
|
}
|
|
|
|
ptr, buf, err := DecodeFrom(reader)
|
|
|
|
by := make([]byte, blobSizeCutoff)
|
|
n, rerr := buf.Read(by)
|
|
by = by[:n]
|
|
|
|
if rerr != nil || (err == nil && len(by) < 512) {
|
|
err = errors.NewCleanPointerError(ptr, by)
|
|
return
|
|
}
|
|
|
|
var from io.Reader = bytes.NewReader(by)
|
|
if fileSize < 0 || int64(len(by)) < fileSize {
|
|
// If there is still more data to be read from the file, tack on
|
|
// the original reader and continue the read from there.
|
|
from = io.MultiReader(from, reader)
|
|
}
|
|
|
|
size, err = tools.CopyWithCallback(writer, from, fileSize, cb)
|
|
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
oid = hex.EncodeToString(oidHash.Sum(nil))
|
|
return
|
|
}
|
|
|
|
func (a *cleanedAsset) Teardown() error {
|
|
return os.Remove(a.Filename)
|
|
}
|