github.com/git-lfs/git-lfs@v2.5.2+incompatible/lfs/pointer.go (about)

     1  package lfs
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"regexp"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/git-lfs/git-lfs/errors"
    15  )
    16  
    17  var (
    18  	v1Aliases = []string{
    19  		"http://git-media.io/v/2",            // alpha
    20  		"https://hawser.github.com/spec/v1",  // pre-release
    21  		"https://git-lfs.github.com/spec/v1", // public launch
    22  	}
    23  	latest      = "https://git-lfs.github.com/spec/v1"
    24  	oidType     = "sha256"
    25  	oidRE       = regexp.MustCompile(`\A[[:alnum:]]{64}`)
    26  	matcherRE   = regexp.MustCompile("git-media|hawser|git-lfs")
    27  	extRE       = regexp.MustCompile(`\Aext-\d{1}-\w+`)
    28  	pointerKeys = []string{"version", "oid", "size"}
    29  )
    30  
    31  type Pointer struct {
    32  	Version    string
    33  	Oid        string
    34  	Size       int64
    35  	OidType    string
    36  	Extensions []*PointerExtension
    37  }
    38  
    39  // A PointerExtension is parsed from the Git LFS Pointer file.
    40  type PointerExtension struct {
    41  	Name     string
    42  	Priority int
    43  	Oid      string
    44  	OidType  string
    45  }
    46  
    47  type ByPriority []*PointerExtension
    48  
    49  func (p ByPriority) Len() int           { return len(p) }
    50  func (p ByPriority) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
    51  func (p ByPriority) Less(i, j int) bool { return p[i].Priority < p[j].Priority }
    52  
    53  func NewPointer(oid string, size int64, exts []*PointerExtension) *Pointer {
    54  	return &Pointer{latest, oid, size, oidType, exts}
    55  }
    56  
    57  func NewPointerExtension(name string, priority int, oid string) *PointerExtension {
    58  	return &PointerExtension{name, priority, oid, oidType}
    59  }
    60  
    61  func (p *Pointer) Encode(writer io.Writer) (int, error) {
    62  	return EncodePointer(writer, p)
    63  }
    64  
    65  func (p *Pointer) Encoded() string {
    66  	if p.Size == 0 {
    67  		return ""
    68  	}
    69  
    70  	var buffer bytes.Buffer
    71  	buffer.WriteString(fmt.Sprintf("version %s\n", latest))
    72  	for _, ext := range p.Extensions {
    73  		buffer.WriteString(fmt.Sprintf("ext-%d-%s %s:%s\n", ext.Priority, ext.Name, ext.OidType, ext.Oid))
    74  	}
    75  	buffer.WriteString(fmt.Sprintf("oid %s:%s\n", p.OidType, p.Oid))
    76  	buffer.WriteString(fmt.Sprintf("size %d\n", p.Size))
    77  	return buffer.String()
    78  }
    79  
    80  func EncodePointer(writer io.Writer, pointer *Pointer) (int, error) {
    81  	return writer.Write([]byte(pointer.Encoded()))
    82  }
    83  
    84  func DecodePointerFromFile(file string) (*Pointer, error) {
    85  	// Check size before reading
    86  	stat, err := os.Stat(file)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	if stat.Size() > blobSizeCutoff {
    91  		return nil, errors.NewNotAPointerError(errors.New("file size exceeds lfs pointer size cutoff"))
    92  	}
    93  	f, err := os.OpenFile(file, os.O_RDONLY, 0644)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	defer f.Close()
    98  	return DecodePointer(f)
    99  }
   100  func DecodePointer(reader io.Reader) (*Pointer, error) {
   101  	p, _, err := DecodeFrom(reader)
   102  	return p, err
   103  }
   104  
   105  // DecodeFrom decodes an *lfs.Pointer from the given io.Reader, "reader".
   106  // If the pointer encoded in the reader could successfully be read and decoded,
   107  // it will be returned with a nil error.
   108  //
   109  // If the pointer could not be decoded, an io.Reader containing the entire
   110  // blob's data will be returned, along with a parse error.
   111  func DecodeFrom(reader io.Reader) (*Pointer, io.Reader, error) {
   112  	buf := make([]byte, blobSizeCutoff)
   113  	n, err := reader.Read(buf)
   114  	buf = buf[:n]
   115  
   116  	var contents io.Reader = bytes.NewReader(buf)
   117  	if err != io.EOF {
   118  		contents = io.MultiReader(contents, reader)
   119  	}
   120  
   121  	if err != nil && err != io.EOF {
   122  		return nil, contents, err
   123  	}
   124  
   125  	p, err := decodeKV(bytes.TrimSpace(buf))
   126  	return p, contents, err
   127  }
   128  
   129  func verifyVersion(version string) error {
   130  	if len(version) == 0 {
   131  		return errors.NewNotAPointerError(errors.New("Missing version"))
   132  	}
   133  
   134  	for _, v := range v1Aliases {
   135  		if v == version {
   136  			return nil
   137  		}
   138  	}
   139  
   140  	return errors.New("Invalid version: " + version)
   141  }
   142  
   143  func decodeKV(data []byte) (*Pointer, error) {
   144  	kvps, exts, err := decodeKVData(data)
   145  	if err != nil {
   146  		if errors.IsBadPointerKeyError(err) {
   147  			return nil, errors.StandardizeBadPointerError(err)
   148  		}
   149  		return nil, err
   150  	}
   151  
   152  	if err := verifyVersion(kvps["version"]); err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	value, ok := kvps["oid"]
   157  	if !ok {
   158  		return nil, errors.New("Invalid Oid")
   159  	}
   160  
   161  	oid, err := parseOid(value)
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	value, ok = kvps["size"]
   167  	size, err := strconv.ParseInt(value, 10, 0)
   168  	if err != nil || size < 0 {
   169  		return nil, fmt.Errorf("Invalid size: %q", value)
   170  	}
   171  
   172  	var extensions []*PointerExtension
   173  	if exts != nil {
   174  		for key, value := range exts {
   175  			ext, err := parsePointerExtension(key, value)
   176  			if err != nil {
   177  				return nil, err
   178  			}
   179  			extensions = append(extensions, ext)
   180  		}
   181  		if err = validatePointerExtensions(extensions); err != nil {
   182  			return nil, err
   183  		}
   184  		sort.Sort(ByPriority(extensions))
   185  	}
   186  
   187  	return NewPointer(oid, size, extensions), nil
   188  }
   189  
   190  func parseOid(value string) (string, error) {
   191  	parts := strings.SplitN(value, ":", 2)
   192  	if len(parts) != 2 {
   193  		return "", errors.New("Invalid Oid value: " + value)
   194  	}
   195  	if parts[0] != oidType {
   196  		return "", errors.New("Invalid Oid type: " + parts[0])
   197  	}
   198  	oid := parts[1]
   199  	if !oidRE.Match([]byte(oid)) {
   200  		return "", errors.New("Invalid Oid: " + oid)
   201  	}
   202  	return oid, nil
   203  }
   204  
   205  func parsePointerExtension(key string, value string) (*PointerExtension, error) {
   206  	keyParts := strings.SplitN(key, "-", 3)
   207  	if len(keyParts) != 3 || keyParts[0] != "ext" {
   208  		return nil, errors.New("Invalid extension value: " + value)
   209  	}
   210  
   211  	p, err := strconv.Atoi(keyParts[1])
   212  	if err != nil || p < 0 {
   213  		return nil, errors.New("Invalid priority: " + keyParts[1])
   214  	}
   215  
   216  	name := keyParts[2]
   217  
   218  	oid, err := parseOid(value)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  
   223  	return NewPointerExtension(name, p, oid), nil
   224  }
   225  
   226  func validatePointerExtensions(exts []*PointerExtension) error {
   227  	m := make(map[int]struct{})
   228  	for _, ext := range exts {
   229  		if _, exist := m[ext.Priority]; exist {
   230  			return fmt.Errorf("Duplicate priority found: %d", ext.Priority)
   231  		}
   232  		m[ext.Priority] = struct{}{}
   233  	}
   234  	return nil
   235  }
   236  
   237  func decodeKVData(data []byte) (kvps map[string]string, exts map[string]string, err error) {
   238  	kvps = make(map[string]string)
   239  
   240  	if !matcherRE.Match(data) {
   241  		err = errors.NewNotAPointerError(errors.New("invalid header"))
   242  		return
   243  	}
   244  
   245  	scanner := bufio.NewScanner(bytes.NewBuffer(data))
   246  	line := 0
   247  	numKeys := len(pointerKeys)
   248  	for scanner.Scan() {
   249  		text := scanner.Text()
   250  		if len(text) == 0 {
   251  			continue
   252  		}
   253  
   254  		parts := strings.SplitN(text, " ", 2)
   255  		if len(parts) < 2 {
   256  			err = fmt.Errorf("Error reading line %d: %s", line, text)
   257  			return
   258  		}
   259  
   260  		key := parts[0]
   261  		value := parts[1]
   262  
   263  		if numKeys <= line {
   264  			err = fmt.Errorf("Extra line: %s", text)
   265  			return
   266  		}
   267  
   268  		if expected := pointerKeys[line]; key != expected {
   269  			if !extRE.Match([]byte(key)) {
   270  				err = errors.NewBadPointerKeyError(expected, key)
   271  				return
   272  			}
   273  			if exts == nil {
   274  				exts = make(map[string]string)
   275  			}
   276  			exts[key] = value
   277  			continue
   278  		}
   279  
   280  		line += 1
   281  		kvps[key] = value
   282  	}
   283  
   284  	err = scanner.Err()
   285  	return
   286  }