Files
aptly/files/package_pool.go
Andrey Smirnov 516dd7b044 Switch to gometalinter
Only small amount of required checks is enabled,
plan is to enable more linters as issues are fixed in the code.
2017-03-23 01:51:08 +03:00

170 lines
3.5 KiB
Go

package files
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"sync"
"github.com/smira/aptly/aptly"
)
// PackagePool is deduplicated storage of package files on filesystem
type PackagePool struct {
sync.Mutex
rootPath string
}
// Check interface
var (
_ aptly.PackagePool = (*PackagePool)(nil)
)
// NewPackagePool creates new instance of PackagePool which specified root
func NewPackagePool(root string) *PackagePool {
return &PackagePool{rootPath: filepath.Join(root, "pool")}
}
// RelativePath returns path relative to pool's root for package files given MD5 and original filename
func (pool *PackagePool) RelativePath(filename string, hashMD5 string) (string, error) {
filename = filepath.Base(filename)
if filename == "." || filename == "/" {
return "", fmt.Errorf("filename %s is invalid", filename)
}
if len(hashMD5) < 4 {
return "", fmt.Errorf("unable to compute pool location for filename %v, MD5 is missing", filename)
}
return filepath.Join(hashMD5[0:2], hashMD5[2:4], filename), nil
}
// Path returns full path to package file in pool given any name and hash of file contents
func (pool *PackagePool) Path(filename string, hashMD5 string) (string, error) {
relative, err := pool.RelativePath(filename, hashMD5)
if err != nil {
return "", err
}
return filepath.Join(pool.rootPath, relative), nil
}
// FilepathList returns file paths of all the files in the pool
func (pool *PackagePool) FilepathList(progress aptly.Progress) ([]string, error) {
pool.Lock()
defer pool.Unlock()
dirs, err := ioutil.ReadDir(pool.rootPath)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, err
}
if len(dirs) == 0 {
return nil, nil
}
if progress != nil {
progress.InitBar(int64(len(dirs)), false)
defer progress.ShutdownBar()
}
result := []string{}
for _, dir := range dirs {
err = filepath.Walk(filepath.Join(pool.rootPath, dir.Name()), func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
result = append(result, path[len(pool.rootPath)+1:])
}
return nil
})
if err != nil {
return nil, err
}
if progress != nil {
progress.AddBar(1)
}
}
return result, nil
}
// Remove deletes file in package pool returns its size
func (pool *PackagePool) Remove(path string) (size int64, err error) {
pool.Lock()
defer pool.Unlock()
path = filepath.Join(pool.rootPath, path)
info, err := os.Stat(path)
if err != nil {
return 0, err
}
err = os.Remove(path)
return info.Size(), err
}
// Import copies file into package pool
func (pool *PackagePool) Import(path string, hashMD5 string) error {
pool.Lock()
defer pool.Unlock()
source, err := os.Open(path)
if err != nil {
return err
}
defer source.Close()
sourceInfo, err := source.Stat()
if err != nil {
return err
}
poolPath, err := pool.Path(path, hashMD5)
if err != nil {
return err
}
targetInfo, err := os.Stat(poolPath)
if err != nil {
if !os.IsNotExist(err) {
// unable to stat target location?
return err
}
} else {
// target already exists
if targetInfo.Size() != sourceInfo.Size() {
// trying to overwrite file?
return fmt.Errorf("unable to import into pool: file %s already exists", poolPath)
}
// assume that target is already there
return nil
}
// create subdirs as necessary
err = os.MkdirAll(filepath.Dir(poolPath), 0777)
if err != nil {
return err
}
target, err := os.Create(poolPath)
if err != nil {
return err
}
defer target.Close()
_, err = io.Copy(target, source)
return err
}