Welcome to mirror list, hosted at ThFree Co, Russian Federation.

zip_storage.go « storage « internal - gitlab.com/gitlab-org/gitlab-pages.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 2511a627c890ea9a19a4b412fa11bc1f42f4d731 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package storage

import (
	"archive/zip"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"gitlab.com/gitlab-org/gitlab-pages/internal/client"
)

const zipDeployPath = "public"
const maxSymlinkSize = 4096
const maxSymlinkDepth = 3

type zipStorage struct {
	*client.LookupPath

	archive *zip.ReadCloser
}

func (z *zipStorage) find(path string) *zip.File {
	// This is O(n) search, very, very, very slow
	for _, file := range z.archive.File {
		if file.Name == path || file.Name == path+"/" {
			return file
		}
	}

	return nil
}

func (z *zipStorage) readSymlink(file *zip.File) (string, error) {
	fi := file.FileInfo()

	if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
		return "", nil
	}

	if fi.Size() > maxSymlinkSize {
		return "", errors.New("symlink size too long")
	}

	rc, err := file.Open()
	if err != nil {
		return "", err
	}
	defer rc.Close()

	data, err := ioutil.ReadAll(rc)
	if err != nil {
		return "", err
	}

	// resolve symlink location relative to current file
	targetPath, err := filepath.Rel(filepath.Dir(file.Name), string(data))
	if err != nil {
		return "", err
	}

	return targetPath, nil
}

func (z *zipStorage) resolveUnchecked(path string) (*zip.File, error) {
	// limit the resolve depth of symlink
	for depth := 0; depth < maxSymlinkDepth; depth++ {
		file := z.find(path)
		if file == nil {
			break
		}

		targetPath, err := z.readSymlink(file)
		if err != nil {
			return nil, err
		}

		// not a symlink
		if targetPath == "" {
			return file, nil
		}

		path = targetPath
	}

	return nil, fmt.Errorf("%q: not found", path)
}

func (z *zipStorage) resolvePublic(path string) (string, *zip.File, error) {
	path = filepath.Join(zipDeployPath, path)
	file, err := z.resolveUnchecked(path)
	if err != nil {
		return "", nil, err
	}

	if !strings.HasPrefix(file.Name, zipDeployPath+"/") {
		return "", nil, fmt.Errorf("%q: is not in %s/", file.Name, zipDeployPath)
	}

	return file.Name[len(zipDeployPath)+1:], file, nil
}

func (z *zipStorage) Resolve(path string) (string, error) {
	targetPath, _, err := z.resolvePublic(path)
	if err != nil {
		println("Resolve", path, "ERROR=", err.Error())
	} else {
		println("Resolve", path, "TARGET_PATH=", targetPath)
	}
	return targetPath, err
}

func (z *zipStorage) Stat(path string) (os.FileInfo, error) {
	_, file, err := z.resolvePublic(path)
	if err != nil {
		println("Stat", path, "ERROR=", err.Error())
		return nil, err
	}

	println("Stat", path, "FILE=", file.Name, file.FileInfo())
	return file.FileInfo(), nil
}

func (z *zipStorage) Open(path string) (File, os.FileInfo, error) {
	_, file, err := z.resolvePublic(path)
	if err != nil {
		println("Open", path, "ERROR=", err.Error())
		return nil, nil, err
	}

	rc, err := file.Open()
	if err != nil {
		println("Open", path, "ERROR=", err.Error())
		return nil, nil, err
	}

	println("Open", path, "FILE=", file.Name, file.FileInfo())
	return rc, file.FileInfo(), nil
}

func (z *zipStorage) Close() {
	println("Close")
	z.archive.Close()
}

func newZipStorage(lookupPath *client.LookupPath) (S, error) {
	archive, err := zip.OpenReader(lookupPath.ArchivePath)
	if err != nil {
		return nil, err
	}

	return &zipStorage{LookupPath: lookupPath, archive: archive}, nil
}