| // 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)) |
| } |