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