func (s *ManifestStore) GetStoreFileReader(repo, tag string) (base.FileReader, error) {
	fileName := encodeRepoTag(repo, tag)
	return s.backend.NewFileOp().AcceptState(s.cacheState).GetFileReader(fileName)
}
GetStoreFileReader returns a FileReader for a file in store directory.
package storage
import (
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"regexp"
	"strings"
	"github.com/uber/makisu/lib/log"
	"github.com/uber/makisu/lib/storage/base"
	"github.com/andres-erbsen/clock"
)
type manifestState int
const (
	manifestDownloadDir = "manifest/download"
	manifestCacheDir    = "manifest/cache"
)
const manifestLRUSize = 16
type ManifestStore struct {
	backend       base.FileStore
	downloadState base.FileState
	cacheState    base.FileState
}
func NewManifestStore(rootdir string) (*ManifestStore, error) {
	downloadDir := path.Join(rootdir, manifestDownloadDir)
	cacheDir := path.Join(rootdir, manifestCacheDir)
	os.RemoveAll(downloadDir)
	if err := os.MkdirAll(downloadDir, 0755); err != nil {
		log.Fatalf("Failed to create manifest download dir %s: %s", downloadDir, err)
	}
	if err := os.MkdirAll(cacheDir, 0755); err != nil {
		log.Fatalf("Failed to create manifest cache dir %s: %s", cacheDir, err)
	}
	backend := base.NewLRUFileStore(manifestLRUSize, clock.New())
	downloadState := base.NewFileState(downloadDir)
	cacheState := base.NewFileState(cacheDir)
	files, err := ioutil.ReadDir(cacheDir)
	if err != nil {
		log.Fatalf("Failed to scan manifest cache dir %s: %s", cacheDir, err)
	}
	for _, f := range files {
		if _, err := backend.NewFileOp().AcceptState(cacheState).GetFileStat(f.Name()); err != nil {
			log.Warnf("Failed to load cached manifest: %s", err)
			if err := backend.NewFileOp().AcceptState(cacheState).DeleteFile(f.Name()); err != nil {
				log.Warnf("Failed to cleanup cached manifest: %s", err)
			}
		}
	}
	return &ManifestStore{
		backend:       backend,
		downloadState: downloadState,
		cacheState:    cacheState,
	}, nil
}
func encodeRepoTag(repo, tag string) string {
	unencoded := strings.Join([]string{repo, tag}, "/")
	return base64.StdEncoding.EncodeToString([]byte(unencoded))
}
func decodeRepoTag(fileName string) (string, string, error) {
	decoded, err := base64.StdEncoding.DecodeString(fileName)
	if err != nil {
		return "", "", err
	}
	parts := regexp.MustCompile(`^(.+)\/(\w+)$`).FindStringSubmatch(string(decoded))
	if parts == nil || len(parts) != 3 {
		return "", "", fmt.Errorf("Failed to parse repo/tag from file name")
	}
	return parts[1], parts[2], nil
}
func (s *ManifestStore) CreateDownloadFile(repo, tag string, len int64) error {
	fileName := encodeRepoTag(repo, tag)
	return s.backend.NewFileOp().AcceptState(s.downloadState).CreateFile(
		fileName, s.downloadState, len)
}
func (s *ManifestStore) GetDownloadFileReadWriter(repo, tag string) (base.FileReadWriter, error) {
	fileName := encodeRepoTag(repo, tag)
	return s.backend.NewFileOp().AcceptState(s.downloadState).GetFileReadWriter(fileName)
}
func (s *ManifestStore) MoveDownloadFileToStore(repo, tag string) error {
	fileName := encodeRepoTag(repo, tag)
	return s.backend.NewFileOp().AcceptState(s.downloadState).MoveFile(fileName, s.cacheState)
}
func (s *ManifestStore) LinkStoreFileFrom(repo, tag, src string) error {
	fileName := encodeRepoTag(repo, tag)
	return s.backend.NewFileOp().AcceptState(s.cacheState).MoveFileFrom(fileName, s.cacheState, src)
}