lfs.go 1.99 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
/*
In this file we handle git lfs objects downloads and uploads
*/

package main

import (
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"io"
12
	"io/ioutil"
13 14 15 16 17 18 19 20 21 22
	"net/http"
	"os"
	"path/filepath"
)

var (
	errHashMismatch = errors.New("Content hash does not match OID")
	errSizeMismatch = errors.New("Content size does not match")
)

23 24
func lfsAuthorizeHandler(handleFunc serviceHandleFunc) serviceHandleFunc {
	return preAuthorizeHandler(func(w http.ResponseWriter, r *gitRequest) {
25

26 27 28 29
		if r.StoreLFSPath == "" {
			fail500(w, "lfsAuthorizeHandler", errors.New("Don't know where to store object, no store path specified."))
			return
		}
30

31 32 33 34 35
		if r.LfsOid == "" {
			fail500(w, "lfsAuthorizeHandler", errors.New("Lfs object oid not specified."))
			return
		}

Marin Jankovski's avatar
Marin Jankovski committed
36
		if err := os.MkdirAll(r.StoreLFSPath, 0700); err != nil {
Marin Jankovski's avatar
Marin Jankovski committed
37 38
			fail500(w, "Couldn't create directory for storing LFS tmp objects.", err)
			return
39 40
		}

41
		handleFunc(w, r)
42
	}, "/authorize")
43
}
44

45
func handleStoreLfsObject(w http.ResponseWriter, r *gitRequest) {
Marin Jankovski's avatar
Marin Jankovski committed
46
	file, err := ioutil.TempFile(r.StoreLFSPath, r.LfsOid)
47 48 49 50
	if err != nil {
		fail500(w, "Couldn't open tmp file for writing.", err)
		return
	}
Marin Jankovski's avatar
Marin Jankovski committed
51
	defer os.Remove(file.Name())
52
	defer file.Close()
53

54 55
	hash := sha256.New()
	hw := io.MultiWriter(hash, file)
56

57
	written, err := io.Copy(hw, r.Body)
58 59 60 61 62
	if err != nil {
		fail500(w, "Failed to save received LFS object.", err)
		return
	}
	file.Close()
63

64 65 66 67
	if written != r.LfsSize {
		fail500(w, "Inconsistent size: ", errSizeMismatch)
		return
	}
68

69 70 71 72
	shaStr := hex.EncodeToString(hash.Sum(nil))
	if shaStr != r.LfsOid {
		fail500(w, "Inconsistent size: ", errSizeMismatch)
		return
73
	}
74
	r.Header.Set("X-GitLab-Lfs-Tmp", filepath.Base(file.Name()))
75

76
	storeReq, err := r.u.newUpstreamRequest(r.Request, nil, "")
77
	if err != nil {
78
		fail500(w, "newUpstreamRequestLfsStoreCallback", err)
79
		return
80 81
	}

82
	storeResponse, err := r.u.httpClient.Do(storeReq)
83
	if err != nil {
84
		fail500(w, "doRequestLfsStoreCallback", err)
85 86
		return
	}
87
	defer storeResponse.Body.Close()
88

89
	forwardResponseToClient(w, storeResponse)
90
}