2017-05-15 21:52:21 +00:00
|
|
|
package odb
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/hex"
|
|
|
|
"io"
|
2017-05-16 21:02:37 +00:00
|
|
|
"io/ioutil"
|
2017-05-15 21:52:21 +00:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2017-05-16 21:02:37 +00:00
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
2017-05-15 21:52:21 +00:00
|
|
|
)
|
|
|
|
|
2017-05-16 21:29:03 +00:00
|
|
|
// fileStorer implements the storer interface by writing to the .git/objects
|
2017-05-15 21:52:21 +00:00
|
|
|
// directory on disc.
|
2017-05-16 21:29:03 +00:00
|
|
|
type fileStorer struct {
|
2017-05-15 21:52:21 +00:00
|
|
|
// root is the top level /objects directory's path on disc.
|
|
|
|
root string
|
|
|
|
}
|
|
|
|
|
2017-05-16 21:29:03 +00:00
|
|
|
// NewFileStorer returns a new fileStorer instance with the given root.
|
|
|
|
func newFileStorer(root string) *fileStorer {
|
|
|
|
return &fileStorer{
|
2017-05-15 21:52:21 +00:00
|
|
|
root: root,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-16 21:29:03 +00:00
|
|
|
// Open implements the storer.Open function, and returns a io.ReadWriteCloser
|
2017-05-15 21:52:21 +00:00
|
|
|
// for the given SHA. If the file does not exist, or if there was any other
|
|
|
|
// error in opening the file, an error will be returned.
|
|
|
|
//
|
|
|
|
// It is the caller's responsibility to close the given file "f" after its use
|
|
|
|
// is complete.
|
2017-05-16 21:29:03 +00:00
|
|
|
func (fs *fileStorer) Open(sha []byte) (f io.ReadWriteCloser, err error) {
|
2017-05-15 21:52:21 +00:00
|
|
|
return fs.open(fs.path(sha), os.O_RDONLY)
|
|
|
|
}
|
|
|
|
|
2017-05-16 21:29:03 +00:00
|
|
|
// Store implements the storer.Store function and returns the number of bytes
|
2017-05-16 21:02:37 +00:00
|
|
|
// written, along with any error encountered in copying the given io.Reader, "r"
|
|
|
|
// into the object database on disk at a path given by "sha".
|
2017-05-15 21:52:21 +00:00
|
|
|
//
|
|
|
|
// If the file already exists, could not be created, or opened, an error will be
|
|
|
|
// returned.
|
2017-05-16 21:29:03 +00:00
|
|
|
func (fs *fileStorer) Store(sha []byte, r io.Reader) (n int64, err error) {
|
2017-05-16 21:02:37 +00:00
|
|
|
tmp, err := ioutil.TempFile("", "")
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
n, err = io.Copy(tmp, r)
|
|
|
|
if err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
2017-05-15 21:52:21 +00:00
|
|
|
path := fs.path(sha)
|
|
|
|
|
2017-05-16 21:02:37 +00:00
|
|
|
if _, err := os.Stat(path); os.IsExist(err) {
|
|
|
|
return n, errors.Errorf("git/odb: file storer cannot copy into file %q, which already exists", path)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = os.Rename(tmp.Name(), path); err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = tmp.Close(); err != nil {
|
|
|
|
return n, err
|
2017-05-15 21:52:21 +00:00
|
|
|
}
|
2017-05-16 21:02:37 +00:00
|
|
|
return n, nil
|
2017-05-15 21:52:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// open opens a given file.
|
2017-05-16 21:29:03 +00:00
|
|
|
func (fs *fileStorer) open(path string, flag int) (*os.File, error) {
|
2017-05-15 21:52:21 +00:00
|
|
|
return os.OpenFile(path, flag, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
// path returns an absolute path on disk to the object given by the OID "sha".
|
2017-05-16 21:29:03 +00:00
|
|
|
func (fs *fileStorer) path(sha []byte) string {
|
2017-05-15 21:52:21 +00:00
|
|
|
encoded := hex.EncodeToString(sha)
|
|
|
|
|
|
|
|
return filepath.Join(fs.root, encoded[:2], encoded[2:])
|
|
|
|
}
|