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

gitlab.com/gitlab-org/gitlab-pages.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKamil Trzciński <ayufan@ayufan.eu>2019-03-01 13:47:12 +0300
committerKamil Trzciński <ayufan@ayufan.eu>2019-03-01 13:47:12 +0300
commit89e88540533def25a6e4ec6e7c1dc1b5b74d3db8 (patch)
treef0315a2b0931ad121bd1e79903dab8f2538999b2 /internal/storage
parentda03df956f659db8121991b330af26929a69cd2e (diff)
Support Zip archive
Diffstat (limited to 'internal/storage')
-rw-r--r--internal/storage/storage.go2
-rw-r--r--internal/storage/zip_storage.go110
2 files changed, 108 insertions, 4 deletions
diff --git a/internal/storage/storage.go b/internal/storage/storage.go
index 58e1cd49..5f767479 100644
--- a/internal/storage/storage.go
+++ b/internal/storage/storage.go
@@ -12,7 +12,7 @@ import (
// to interact with the file, to read, stat, and seek
type File interface {
io.Reader
- io.Seeker
+ //io.Seeker
io.Closer
}
diff --git a/internal/storage/zip_storage.go b/internal/storage/zip_storage.go
index d658d8b1..8f964e94 100644
--- a/internal/storage/zip_storage.go
+++ b/internal/storage/zip_storage.go
@@ -3,27 +3,131 @@ 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 {
+ 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) {
- return "", errors.New("not supported")
+ targetPath, _, err := z.resolvePublic(path)
+ return targetPath, err
}
func (z *zipStorage) Stat(path string) (os.FileInfo, error) {
- return nil, errors.New("not supported")
+ _, file, err := z.resolvePublic(path)
+ if err != nil {
+ return nil, err
+ }
+
+ return file.FileInfo(), nil
}
func (z *zipStorage) Open(path string) (File, os.FileInfo, error) {
- return nil, nil, errors.New("not supported")
+ _, file, err := z.resolvePublic(path)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ rc, err := file.Open()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return rc, file.FileInfo(), nil
}
func (z *zipStorage) Close() {