mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-01-12 03:21:33 +00:00
Only small amount of required checks is enabled, plan is to enable more linters as issues are fixed in the code.
170 lines
3.5 KiB
Go
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
|
|
}
|