blob: f5100fb54ed40e3a49aa7dc28a413731abf23031 [file] [log] [blame]
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cache
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
git "github.com/libgit2/git2go"
)
// CAS is a content addressable storage. It is intended to be used
// with git SHA1 data. It stores blobs as uncompressed files without
// git headers. This means that we can wire up files from the CAS
// directly with a FUSE file system.
type CAS struct {
dir string
}
// NewCAS creates a new CAS object.
func NewCAS(dir string) (*CAS, error) {
if err := os.MkdirAll(dir, 0700); err != nil {
return nil, err
}
return &CAS{
dir: dir,
}, nil
}
func (c *CAS) path(id git.Oid) string {
str := id.String()
return fmt.Sprintf("%s/%s/%s", c.dir, str[:3], str[3:])
}
// Open returns a file corresponding to the blob, opened for reading.
func (c *CAS) Open(id git.Oid) (*os.File, bool) {
f, err := os.Open(c.path(id))
return f, err == nil
}
// Write writes the given data under the given ID atomically.
func (c *CAS) Write(id git.Oid, data []byte) error {
// TODO(hanwen): we should run data through the git hash to
// verify that it is what it says it is.
f, err := ioutil.TempFile(c.dir, "tmp")
if err != nil {
return err
}
if err := f.Chmod(0444); err != nil {
return err
}
if _, err := f.Write(data); err != nil {
return err
}
if err := f.Close(); err != nil {
return err
}
p := c.path(id)
dir := filepath.Dir(p)
if err := os.MkdirAll(dir, 0700); err != nil {
return err
}
return os.Rename(f.Name(), c.path(id))
}