blob: 4165a870513d3509f2d0300e54fca3a6d4072f25 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package binarylib
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"v.io/v23/verror"
"v.io/x/lib/vlog"
)
const (
checksumFileName = "checksum"
dataFileName = "data"
lockFileName = "lock"
nameFileName = "name"
mediaInfoFileName = "mediainfo"
)
// checksumExists checks whether the given part path is valid and
// contains a checksum. The implementation uses the existence of
// the path dir to determine whether the part is valid, and the
// existence of checksum to determine whether the binary part
// exists.
func checksumExists(path string) error {
switch _, err := os.Stat(path); {
case os.IsNotExist(err):
return verror.New(ErrInvalidPart, nil, path)
case err != nil:
vlog.Errorf("Stat(%v) failed: %v", path, err)
return verror.New(ErrOperationFailed, nil, path)
}
checksumFile := filepath.Join(path, checksumFileName)
_, err := os.Stat(checksumFile)
switch {
case os.IsNotExist(err):
return verror.New(verror.ErrNoExist, nil, path)
case err != nil:
vlog.Errorf("Stat(%v) failed: %v", checksumFile, err)
return verror.New(ErrOperationFailed, nil, path)
default:
return nil
}
}
// generatePartPath generates a path for the given binary part.
func (i *binaryService) generatePartPath(part int) string {
return generatePartPath(i.path, part)
}
func generatePartPath(dir string, part int) string {
return filepath.Join(dir, fmt.Sprintf("%d", part))
}
// getParts returns a collection of paths to the parts of the binary.
func getParts(path string) ([]string, error) {
infos, err := ioutil.ReadDir(path)
if err != nil {
vlog.Errorf("ReadDir(%v) failed: %v", path, err)
return []string{}, verror.New(ErrOperationFailed, nil, path)
}
nDirs := 0
for _, info := range infos {
if info.IsDir() {
nDirs++
}
}
result := make([]string, nDirs)
for _, info := range infos {
if info.IsDir() {
partName := info.Name()
idx, err := strconv.Atoi(partName)
if err != nil {
vlog.Errorf("Atoi(%v) failed: %v", partName, err)
return []string{}, verror.New(ErrOperationFailed, nil, path)
}
if idx < 0 || idx >= len(infos) || result[idx] != "" {
return []string{}, verror.New(ErrOperationFailed, nil, path)
}
result[idx] = filepath.Join(path, partName)
} else {
if info.Name() == nameFileName || info.Name() == mediaInfoFileName {
continue
}
// The only entries should correspond to the part dirs.
return []string{}, verror.New(ErrOperationFailed, nil, path)
}
}
return result, nil
}
// createObjectNameTree returns a tree of all the valid object names in the
// repository.
func (i *binaryService) createObjectNameTree() *treeNode {
pattern := i.state.rootDir
for d := 0; d < i.state.depth; d++ {
pattern = filepath.Join(pattern, "*")
}
pattern = filepath.Join(pattern, "*", nameFileName)
matches, err := filepath.Glob(pattern)
if err != nil {
return nil
}
tree := newTreeNode()
for _, m := range matches {
name, err := ioutil.ReadFile(m)
if err != nil {
continue
}
elems := strings.Split(string(name), string(filepath.Separator))
tree.find(elems, true)
}
return tree
}
type treeNode struct {
children map[string]*treeNode
}
func newTreeNode() *treeNode {
return &treeNode{children: make(map[string]*treeNode)}
}
func (n *treeNode) find(names []string, create bool) *treeNode {
for {
if len(names) == 0 {
return n
}
if next, ok := n.children[names[0]]; ok {
n = next
names = names[1:]
continue
}
if create {
nn := newTreeNode()
n.children[names[0]] = nn
n = nn
names = names[1:]
continue
}
return nil
}
}