github.com/catandhorse/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 }