mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-07-15 11:57:59 +00:00
New upstream version 1.1.1
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"github.com/smira/aptly/aptly"
|
||||
"github.com/smira/aptly/utils"
|
||||
)
|
||||
|
||||
type mockChecksumStorage struct {
|
||||
store map[string]utils.ChecksumInfo
|
||||
}
|
||||
|
||||
// NewMockChecksumStorage creates aptly.ChecksumStorage for tests
|
||||
func NewMockChecksumStorage() aptly.ChecksumStorage {
|
||||
return &mockChecksumStorage{
|
||||
store: make(map[string]utils.ChecksumInfo),
|
||||
}
|
||||
}
|
||||
|
||||
func (st *mockChecksumStorage) Get(path string) (*utils.ChecksumInfo, error) {
|
||||
c, ok := st.store[path]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (st *mockChecksumStorage) Update(path string, c *utils.ChecksumInfo) error {
|
||||
st.store[path] = *c
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check interface
|
||||
var (
|
||||
_ aptly.ChecksumStorage = &mockChecksumStorage{}
|
||||
)
|
||||
+277
-33
@@ -7,33 +7,51 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/smira/go-uuid/uuid"
|
||||
|
||||
"github.com/smira/aptly/aptly"
|
||||
"github.com/smira/aptly/utils"
|
||||
)
|
||||
|
||||
// PackagePool is deduplicated storage of package files on filesystem
|
||||
type PackagePool struct {
|
||||
sync.Mutex
|
||||
rootPath string
|
||||
|
||||
rootPath string
|
||||
supportLegacyPaths bool
|
||||
}
|
||||
|
||||
// Check interface
|
||||
var (
|
||||
_ aptly.PackagePool = (*PackagePool)(nil)
|
||||
_ aptly.PackagePool = (*PackagePool)(nil)
|
||||
_ aptly.LocalPackagePool = (*PackagePool)(nil)
|
||||
)
|
||||
|
||||
// NewPackagePool creates new instance of PackagePool which specified root
|
||||
func NewPackagePool(root string) *PackagePool {
|
||||
return &PackagePool{rootPath: filepath.Join(root, "pool")}
|
||||
func NewPackagePool(root string, supportLegacyPaths bool) *PackagePool {
|
||||
rootPath := filepath.Join(root, "pool")
|
||||
rootPath, err := filepath.Abs(rootPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &PackagePool{
|
||||
rootPath: rootPath,
|
||||
supportLegacyPaths: supportLegacyPaths,
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// LegacyPath returns path relative to pool's root for pre-1.1 aptly (based on MD5)
|
||||
func (pool *PackagePool) LegacyPath(filename string, checksums *utils.ChecksumInfo) (string, error) {
|
||||
filename = filepath.Base(filename)
|
||||
if filename == "." || filename == "/" {
|
||||
return "", fmt.Errorf("filename %s is invalid", filename)
|
||||
}
|
||||
|
||||
hashMD5 := checksums.MD5
|
||||
|
||||
if len(hashMD5) < 4 {
|
||||
return "", fmt.Errorf("unable to compute pool location for filename %v, MD5 is missing", filename)
|
||||
}
|
||||
@@ -41,14 +59,21 @@ func (pool *PackagePool) RelativePath(filename string, hashMD5 string) (string,
|
||||
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
|
||||
// buildPoolPath generates pool path based on file checksum
|
||||
func (pool *PackagePool) buildPoolPath(filename string, checksums *utils.ChecksumInfo) (string, error) {
|
||||
filename = filepath.Base(filename)
|
||||
if filename == "." || filename == "/" {
|
||||
return "", fmt.Errorf("filename %s is invalid", filename)
|
||||
}
|
||||
|
||||
return filepath.Join(pool.rootPath, relative), nil
|
||||
hash := checksums.SHA256
|
||||
|
||||
if len(hash) < 4 {
|
||||
// this should never happen in real life
|
||||
return "", fmt.Errorf("unable to compute pool location for filename %v, SHA256 is missing", filename)
|
||||
}
|
||||
|
||||
return filepath.Join(hash[0:2], hash[2:4], hash[4:32]+"_"+filename), nil
|
||||
}
|
||||
|
||||
// FilepathList returns file paths of all the files in the pool
|
||||
@@ -113,57 +138,276 @@ func (pool *PackagePool) Remove(path string) (size int64, err error) {
|
||||
return info.Size(), err
|
||||
}
|
||||
|
||||
func (pool *PackagePool) ensureChecksums(poolPath, fullPoolPath string, checksumStorage aptly.ChecksumStorage) (targetChecksums *utils.ChecksumInfo, err error) {
|
||||
targetChecksums, err = checksumStorage.Get(poolPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if targetChecksums == nil {
|
||||
// we don't have checksums stored yet for this file
|
||||
targetChecksums = &utils.ChecksumInfo{}
|
||||
*targetChecksums, err = utils.ChecksumsForFile(fullPoolPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = checksumStorage.Update(poolPath, targetChecksums)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Verify checks whether file exists in the pool and fills back checksum info
|
||||
//
|
||||
// if poolPath is empty, poolPath is generated automatically based on checksum info (if available)
|
||||
// in any case, if function returns true, it also fills back checksums with complete information about the file in the pool
|
||||
func (pool *PackagePool) Verify(poolPath, basename string, checksums *utils.ChecksumInfo, checksumStorage aptly.ChecksumStorage) (string, bool, error) {
|
||||
possiblePoolPaths := []string{}
|
||||
|
||||
if poolPath != "" {
|
||||
possiblePoolPaths = append(possiblePoolPaths, poolPath)
|
||||
} else {
|
||||
// try to guess
|
||||
if checksums.SHA256 != "" {
|
||||
modernPath, err := pool.buildPoolPath(basename, checksums)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
possiblePoolPaths = append(possiblePoolPaths, modernPath)
|
||||
}
|
||||
|
||||
if pool.supportLegacyPaths && checksums.MD5 != "" {
|
||||
legacyPath, err := pool.LegacyPath(basename, checksums)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
possiblePoolPaths = append(possiblePoolPaths, legacyPath)
|
||||
}
|
||||
}
|
||||
|
||||
for _, path := range possiblePoolPaths {
|
||||
fullPoolPath := filepath.Join(pool.rootPath, path)
|
||||
|
||||
targetInfo, err := os.Stat(fullPoolPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
// unable to stat target location?
|
||||
return "", false, err
|
||||
}
|
||||
// doesn't exist, skip it
|
||||
continue
|
||||
}
|
||||
|
||||
if targetInfo.Size() != checksums.Size {
|
||||
// oops, wrong file?
|
||||
continue
|
||||
}
|
||||
|
||||
var targetChecksums *utils.ChecksumInfo
|
||||
targetChecksums, err = pool.ensureChecksums(path, fullPoolPath, checksumStorage)
|
||||
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
if checksums.MD5 != "" && targetChecksums.MD5 != checksums.MD5 ||
|
||||
checksums.SHA256 != "" && targetChecksums.SHA256 != checksums.SHA256 {
|
||||
// wrong file?
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
// fill back checksums
|
||||
*checksums = *targetChecksums
|
||||
return path, true, nil
|
||||
}
|
||||
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
// Import copies file into package pool
|
||||
func (pool *PackagePool) Import(path string, hashMD5 string) error {
|
||||
//
|
||||
// - srcPath is full path to source file as it is now
|
||||
// - basename is desired human-readable name (canonical filename)
|
||||
// - checksums are used to calculate file placement
|
||||
// - move indicates whether srcPath can be removed
|
||||
func (pool *PackagePool) Import(srcPath, basename string, checksums *utils.ChecksumInfo, move bool, checksumStorage aptly.ChecksumStorage) (string, error) {
|
||||
pool.Lock()
|
||||
defer pool.Unlock()
|
||||
|
||||
source, err := os.Open(path)
|
||||
source, err := os.Open(srcPath)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
defer source.Close()
|
||||
|
||||
sourceInfo, err := source.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
poolPath, err := pool.Path(path, hashMD5)
|
||||
if checksums.MD5 == "" || checksums.SHA256 == "" || checksums.Size != sourceInfo.Size() {
|
||||
// need to update checksums, MD5 and SHA256 should be always defined
|
||||
*checksums, err = utils.ChecksumsForFile(srcPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// build target path
|
||||
poolPath, err := pool.buildPoolPath(basename, checksums)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
targetInfo, err := os.Stat(poolPath)
|
||||
fullPoolPath := filepath.Join(pool.rootPath, poolPath)
|
||||
|
||||
targetInfo, err := os.Stat(fullPoolPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
// unable to stat target location?
|
||||
return err
|
||||
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)
|
||||
// target already exists and same size
|
||||
if targetInfo.Size() == sourceInfo.Size() {
|
||||
var targetChecksums *utils.ChecksumInfo
|
||||
|
||||
targetChecksums, err = pool.ensureChecksums(poolPath, fullPoolPath, checksumStorage)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
*checksums = *targetChecksums
|
||||
return poolPath, nil
|
||||
}
|
||||
|
||||
// assume that target is already there
|
||||
return nil
|
||||
// trying to overwrite file?
|
||||
return "", fmt.Errorf("unable to import into pool: file %s already exists", fullPoolPath)
|
||||
}
|
||||
|
||||
if pool.supportLegacyPaths {
|
||||
// file doesn't exist at new location, check legacy location
|
||||
var (
|
||||
legacyTargetInfo os.FileInfo
|
||||
legacyPath, legacyFullPath string
|
||||
)
|
||||
|
||||
legacyPath, err = pool.LegacyPath(basename, checksums)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
legacyFullPath = filepath.Join(pool.rootPath, legacyPath)
|
||||
|
||||
legacyTargetInfo, err = os.Stat(legacyFullPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
// legacy file exists
|
||||
if legacyTargetInfo.Size() == sourceInfo.Size() {
|
||||
// file exists at legacy path and it's same size, consider it's already in the pool
|
||||
var targetChecksums *utils.ChecksumInfo
|
||||
|
||||
targetChecksums, err = pool.ensureChecksums(legacyPath, legacyFullPath, checksumStorage)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
*checksums = *targetChecksums
|
||||
return legacyPath, nil
|
||||
}
|
||||
|
||||
// size is different, import at new path
|
||||
}
|
||||
}
|
||||
|
||||
// create subdirs as necessary
|
||||
err = os.MkdirAll(filepath.Dir(poolPath), 0777)
|
||||
poolDir := filepath.Dir(fullPoolPath)
|
||||
err = os.MkdirAll(poolDir, 0777)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
target, err := os.Create(poolPath)
|
||||
// check if we can use hardlinks instead of copying/moving
|
||||
poolDirInfo, err := os.Stat(poolDir)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
defer target.Close()
|
||||
|
||||
_, err = io.Copy(target, source)
|
||||
if poolDirInfo.Sys().(*syscall.Stat_t).Dev == sourceInfo.Sys().(*syscall.Stat_t).Dev {
|
||||
// same filesystem, try to use hardlink
|
||||
err = os.Link(srcPath, fullPoolPath)
|
||||
} else {
|
||||
err = os.ErrInvalid
|
||||
}
|
||||
|
||||
return err
|
||||
if err != nil {
|
||||
// different filesystems or failed hardlink, fallback to copy
|
||||
var target *os.File
|
||||
target, err = os.Create(fullPoolPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer target.Close()
|
||||
|
||||
_, err = io.Copy(target, source)
|
||||
|
||||
if err == nil {
|
||||
err = target.Close()
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if !checksums.Complete() {
|
||||
// need full checksums here
|
||||
*checksums, err = utils.ChecksumsForFile(srcPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
err = checksumStorage.Update(poolPath, checksums)
|
||||
}
|
||||
|
||||
if err == nil && move {
|
||||
err = os.Remove(srcPath)
|
||||
}
|
||||
|
||||
return poolPath, err
|
||||
}
|
||||
|
||||
// Open returns io.ReadCloser to access the file
|
||||
func (pool *PackagePool) Open(path string) (aptly.ReadSeekerCloser, error) {
|
||||
return os.Open(filepath.Join(pool.rootPath, path))
|
||||
}
|
||||
|
||||
// Stat returns Unix stat(2) info
|
||||
func (pool *PackagePool) Stat(path string) (os.FileInfo, error) {
|
||||
return os.Stat(filepath.Join(pool.rootPath, path))
|
||||
}
|
||||
|
||||
// Link generates hardlink to destination path
|
||||
func (pool *PackagePool) Link(path, dstPath string) error {
|
||||
return os.Link(filepath.Join(pool.rootPath, path), dstPath)
|
||||
}
|
||||
|
||||
// Symlink generates symlink to destination path
|
||||
func (pool *PackagePool) Symlink(path, dstPath string) error {
|
||||
return os.Symlink(filepath.Join(pool.rootPath, path), dstPath)
|
||||
}
|
||||
|
||||
// FullPath generates full path to the file in pool
|
||||
//
|
||||
// Please use with care: it's not supposed to be used to access files
|
||||
func (pool *PackagePool) FullPath(path string) string {
|
||||
return filepath.Join(pool.rootPath, path)
|
||||
}
|
||||
|
||||
// GenerateTempPath generates temporary path for download (which is fast to import into package pool later on)
|
||||
func (pool *PackagePool) GenerateTempPath(filename string) (string, error) {
|
||||
random := uuid.NewRandom().String()
|
||||
|
||||
return filepath.Join(pool.rootPath, random[0:2], random[2:4], random[4:]+filename), nil
|
||||
}
|
||||
|
||||
+267
-32
@@ -1,47 +1,51 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/smira/aptly/aptly"
|
||||
"github.com/smira/aptly/utils"
|
||||
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
type PackagePoolSuite struct {
|
||||
pool *PackagePool
|
||||
pool *PackagePool
|
||||
checksum utils.ChecksumInfo
|
||||
debFile string
|
||||
cs aptly.ChecksumStorage
|
||||
}
|
||||
|
||||
var _ = Suite(&PackagePoolSuite{})
|
||||
|
||||
func (s *PackagePoolSuite) SetUpTest(c *C) {
|
||||
s.pool = NewPackagePool(c.MkDir())
|
||||
|
||||
s.pool = NewPackagePool(c.MkDir(), true)
|
||||
s.checksum = utils.ChecksumInfo{
|
||||
MD5: "0035d7822b2f8f0ec4013f270fd650c2",
|
||||
}
|
||||
_, _File, _, _ := runtime.Caller(0)
|
||||
s.debFile = filepath.Join(filepath.Dir(_File), "../system/files/libboost-program-options-dev_1.49.0.1_i386.deb")
|
||||
s.cs = NewMockChecksumStorage()
|
||||
}
|
||||
|
||||
func (s *PackagePoolSuite) TestRelativePath(c *C) {
|
||||
path, err := s.pool.RelativePath("a/b/package.deb", "91b1a1480b90b9e269ca44d897b12575")
|
||||
func (s *PackagePoolSuite) TestLegacyPath(c *C) {
|
||||
path, err := s.pool.LegacyPath("a/b/package.deb", &s.checksum)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(path, Equals, "91/b1/package.deb")
|
||||
c.Assert(path, Equals, "00/35/package.deb")
|
||||
|
||||
_, err = s.pool.RelativePath("/", "91b1a1480b90b9e269ca44d897b12575")
|
||||
_, err = s.pool.LegacyPath("/", &s.checksum)
|
||||
c.Assert(err, ErrorMatches, ".*is invalid")
|
||||
_, err = s.pool.RelativePath("", "91b1a1480b90b9e269ca44d897b12575")
|
||||
_, err = s.pool.LegacyPath("", &s.checksum)
|
||||
c.Assert(err, ErrorMatches, ".*is invalid")
|
||||
_, err = s.pool.RelativePath("a/b/package.deb", "9")
|
||||
_, err = s.pool.LegacyPath("a/b/package.deb", &utils.ChecksumInfo{MD5: "9"})
|
||||
c.Assert(err, ErrorMatches, ".*MD5 is missing")
|
||||
}
|
||||
|
||||
func (s *PackagePoolSuite) TestPath(c *C) {
|
||||
path, err := s.pool.Path("a/b/package.deb", "91b1a1480b90b9e269ca44d897b12575")
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(path, Equals, filepath.Join(s.pool.rootPath, "91/b1/package.deb"))
|
||||
|
||||
_, err = s.pool.Path("/", "91b1a1480b90b9e269ca44d897b12575")
|
||||
c.Assert(err, ErrorMatches, ".*is invalid")
|
||||
}
|
||||
|
||||
func (s *PackagePoolSuite) TestFilepathList(c *C) {
|
||||
list, err := s.pool.FilepathList(nil)
|
||||
c.Check(err, IsNil)
|
||||
@@ -88,33 +92,264 @@ func (s *PackagePoolSuite) TestRemove(c *C) {
|
||||
}
|
||||
|
||||
func (s *PackagePoolSuite) TestImportOk(c *C) {
|
||||
_, _File, _, _ := runtime.Caller(0)
|
||||
debFile := filepath.Join(filepath.Dir(_File), "../system/files/libboost-program-options-dev_1.49.0.1_i386.deb")
|
||||
|
||||
err := s.pool.Import(debFile, "91b1a1480b90b9e269ca44d897b12575")
|
||||
path, err := s.pool.Import(s.debFile, filepath.Base(s.debFile), &s.checksum, false, s.cs)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(path, Equals, "c7/6b/4bd12fd92e4dfe1b55b18a67a669_libboost-program-options-dev_1.49.0.1_i386.deb")
|
||||
// SHA256 should be automatically calculated
|
||||
c.Check(s.checksum.SHA256, Equals, "c76b4bd12fd92e4dfe1b55b18a67a669d92f62985d6a96c8a21d96120982cf12")
|
||||
// checksum storage is filled with new checksum
|
||||
c.Check(s.cs.(*mockChecksumStorage).store[path].SHA256, Equals, "c76b4bd12fd92e4dfe1b55b18a67a669d92f62985d6a96c8a21d96120982cf12")
|
||||
|
||||
info, err := os.Stat(filepath.Join(s.pool.rootPath, "91", "b1", "libboost-program-options-dev_1.49.0.1_i386.deb"))
|
||||
c.Check(err, IsNil)
|
||||
info, err := s.pool.Stat(path)
|
||||
c.Assert(err, IsNil)
|
||||
c.Check(info.Size(), Equals, int64(2738))
|
||||
c.Check(info.Sys().(*syscall.Stat_t).Nlink > 1, Equals, true)
|
||||
|
||||
// import as different name
|
||||
path, err = s.pool.Import(s.debFile, "some.deb", &s.checksum, false, s.cs)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(path, Equals, "c7/6b/4bd12fd92e4dfe1b55b18a67a669_some.deb")
|
||||
// checksum storage is filled with new checksum
|
||||
c.Check(s.cs.(*mockChecksumStorage).store[path].SHA256, Equals, "c76b4bd12fd92e4dfe1b55b18a67a669d92f62985d6a96c8a21d96120982cf12")
|
||||
|
||||
// double import, should be ok
|
||||
err = s.pool.Import(debFile, "91b1a1480b90b9e269ca44d897b12575")
|
||||
s.checksum.SHA512 = "" // clear checksum
|
||||
path, err = s.pool.Import(s.debFile, filepath.Base(s.debFile), &s.checksum, false, s.cs)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(path, Equals, "c7/6b/4bd12fd92e4dfe1b55b18a67a669_libboost-program-options-dev_1.49.0.1_i386.deb")
|
||||
// checksum is filled back based on checksum storage
|
||||
c.Check(s.checksum.SHA512, Equals, "d7302241373da972aa9b9e71d2fd769b31a38f71182aa71bc0d69d090d452c69bb74b8612c002ccf8a89c279ced84ac27177c8b92d20f00023b3d268e6cec69c")
|
||||
|
||||
// clear checksum storage, and do double-import
|
||||
delete(s.cs.(*mockChecksumStorage).store, path)
|
||||
s.checksum.SHA512 = "" // clear checksum
|
||||
path, err = s.pool.Import(s.debFile, filepath.Base(s.debFile), &s.checksum, false, s.cs)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(path, Equals, "c7/6b/4bd12fd92e4dfe1b55b18a67a669_libboost-program-options-dev_1.49.0.1_i386.deb")
|
||||
// checksum is filled back based on re-calculation of file in the pool
|
||||
c.Check(s.checksum.SHA512, Equals, "d7302241373da972aa9b9e71d2fd769b31a38f71182aa71bc0d69d090d452c69bb74b8612c002ccf8a89c279ced84ac27177c8b92d20f00023b3d268e6cec69c")
|
||||
|
||||
// import under new name, but with checksums already filled in
|
||||
s.checksum.SHA512 = "" // clear checksum
|
||||
path, err = s.pool.Import(s.debFile, "other.deb", &s.checksum, false, s.cs)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(path, Equals, "c7/6b/4bd12fd92e4dfe1b55b18a67a669_other.deb")
|
||||
// checksum is filled back based on re-calculation of source file
|
||||
c.Check(s.checksum.SHA512, Equals, "d7302241373da972aa9b9e71d2fd769b31a38f71182aa71bc0d69d090d452c69bb74b8612c002ccf8a89c279ced84ac27177c8b92d20f00023b3d268e6cec69c")
|
||||
}
|
||||
|
||||
func (s *PackagePoolSuite) TestImportLegacy(c *C) {
|
||||
os.MkdirAll(filepath.Join(s.pool.rootPath, "00", "35"), 0755)
|
||||
err := utils.CopyFile(s.debFile, filepath.Join(s.pool.rootPath, "00", "35", "libboost-program-options-dev_1.49.0.1_i386.deb"))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
s.checksum.Size = 2738
|
||||
var path string
|
||||
path, err = s.pool.Import(s.debFile, filepath.Base(s.debFile), &s.checksum, false, s.cs)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(path, Equals, "00/35/libboost-program-options-dev_1.49.0.1_i386.deb")
|
||||
// checksum is filled back based on checksum storage
|
||||
c.Check(s.checksum.SHA512, Equals, "d7302241373da972aa9b9e71d2fd769b31a38f71182aa71bc0d69d090d452c69bb74b8612c002ccf8a89c279ced84ac27177c8b92d20f00023b3d268e6cec69c")
|
||||
}
|
||||
|
||||
func (s *PackagePoolSuite) TestVerifyLegacy(c *C) {
|
||||
s.checksum.Size = 2738
|
||||
// file doesn't exist yet
|
||||
path, exists, err := s.pool.Verify("", filepath.Base(s.debFile), &s.checksum, s.cs)
|
||||
c.Check(path, Equals, "")
|
||||
c.Check(err, IsNil)
|
||||
c.Check(exists, Equals, false)
|
||||
|
||||
os.MkdirAll(filepath.Join(s.pool.rootPath, "00", "35"), 0755)
|
||||
err = utils.CopyFile(s.debFile, filepath.Join(s.pool.rootPath, "00", "35", "libboost-program-options-dev_1.49.0.1_i386.deb"))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// check existence (and fills back checksum)
|
||||
path, exists, err = s.pool.Verify("", filepath.Base(s.debFile), &s.checksum, s.cs)
|
||||
c.Check(path, Equals, "00/35/libboost-program-options-dev_1.49.0.1_i386.deb")
|
||||
c.Check(err, IsNil)
|
||||
c.Check(exists, Equals, true)
|
||||
c.Check(s.checksum.SHA512, Equals, "d7302241373da972aa9b9e71d2fd769b31a38f71182aa71bc0d69d090d452c69bb74b8612c002ccf8a89c279ced84ac27177c8b92d20f00023b3d268e6cec69c")
|
||||
}
|
||||
|
||||
func (s *PackagePoolSuite) TestVerify(c *C) {
|
||||
// file doesn't exist yet
|
||||
ppath, exists, err := s.pool.Verify("", filepath.Base(s.debFile), &s.checksum, s.cs)
|
||||
c.Check(ppath, Equals, "")
|
||||
c.Check(err, IsNil)
|
||||
c.Check(exists, Equals, false)
|
||||
|
||||
// import file
|
||||
path, err := s.pool.Import(s.debFile, filepath.Base(s.debFile), &s.checksum, false, s.cs)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(path, Equals, "c7/6b/4bd12fd92e4dfe1b55b18a67a669_libboost-program-options-dev_1.49.0.1_i386.deb")
|
||||
|
||||
// check existence
|
||||
ppath, exists, err = s.pool.Verify("", filepath.Base(s.debFile), &s.checksum, s.cs)
|
||||
c.Check(ppath, Equals, ppath)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(exists, Equals, true)
|
||||
c.Check(s.checksum.SHA512, Equals, "d7302241373da972aa9b9e71d2fd769b31a38f71182aa71bc0d69d090d452c69bb74b8612c002ccf8a89c279ced84ac27177c8b92d20f00023b3d268e6cec69c")
|
||||
|
||||
// check existence with fixed path
|
||||
ppath, exists, err = s.pool.Verify(path, filepath.Base(s.debFile), &s.checksum, s.cs)
|
||||
c.Check(ppath, Equals, path)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(exists, Equals, true)
|
||||
c.Check(s.checksum.SHA512, Equals, "d7302241373da972aa9b9e71d2fd769b31a38f71182aa71bc0d69d090d452c69bb74b8612c002ccf8a89c279ced84ac27177c8b92d20f00023b3d268e6cec69c")
|
||||
|
||||
// check existence, but with missing checksum
|
||||
s.checksum.SHA512 = ""
|
||||
ppath, exists, err = s.pool.Verify("", filepath.Base(s.debFile), &s.checksum, s.cs)
|
||||
c.Check(ppath, Equals, path)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(exists, Equals, true)
|
||||
// checksum is filled back based on checksum storage
|
||||
c.Check(s.checksum.SHA512, Equals, "d7302241373da972aa9b9e71d2fd769b31a38f71182aa71bc0d69d090d452c69bb74b8612c002ccf8a89c279ced84ac27177c8b92d20f00023b3d268e6cec69c")
|
||||
|
||||
// check existence, with missing checksum info but correct path and size available
|
||||
ck := utils.ChecksumInfo{
|
||||
Size: s.checksum.Size,
|
||||
}
|
||||
ppath, exists, err = s.pool.Verify(path, filepath.Base(s.debFile), &ck, s.cs)
|
||||
c.Check(ppath, Equals, path)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(exists, Equals, true)
|
||||
// checksum is filled back based on checksum storage
|
||||
c.Check(ck.SHA512, Equals, "d7302241373da972aa9b9e71d2fd769b31a38f71182aa71bc0d69d090d452c69bb74b8612c002ccf8a89c279ced84ac27177c8b92d20f00023b3d268e6cec69c")
|
||||
|
||||
// check existence, with wrong checksum info but correct path and size available
|
||||
ck.SHA256 = "abc"
|
||||
ppath, exists, err = s.pool.Verify(path, filepath.Base(s.debFile), &ck, s.cs)
|
||||
c.Check(ppath, Equals, "")
|
||||
c.Check(err, IsNil)
|
||||
c.Check(exists, Equals, false)
|
||||
|
||||
// check existence, with missing checksum and no info in checksum storage
|
||||
delete(s.cs.(*mockChecksumStorage).store, path)
|
||||
s.checksum.SHA512 = ""
|
||||
ppath, exists, err = s.pool.Verify("", filepath.Base(s.debFile), &s.checksum, s.cs)
|
||||
c.Check(ppath, Equals, path)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(exists, Equals, true)
|
||||
// checksum is filled back based on re-calculation
|
||||
c.Check(s.checksum.SHA512, Equals, "d7302241373da972aa9b9e71d2fd769b31a38f71182aa71bc0d69d090d452c69bb74b8612c002ccf8a89c279ced84ac27177c8b92d20f00023b3d268e6cec69c")
|
||||
|
||||
// check existence, with wrong size
|
||||
s.checksum.Size = 13455
|
||||
ppath, exists, err = s.pool.Verify("", filepath.Base(s.debFile), &s.checksum, s.cs)
|
||||
c.Check(ppath, Equals, "")
|
||||
c.Check(err, IsNil)
|
||||
c.Check(exists, Equals, false)
|
||||
|
||||
// check existence, with empty checksum info
|
||||
ppath, exists, err = s.pool.Verify("", filepath.Base(s.debFile), &utils.ChecksumInfo{}, s.cs)
|
||||
c.Check(ppath, Equals, "")
|
||||
c.Check(err, IsNil)
|
||||
c.Check(exists, Equals, false)
|
||||
}
|
||||
|
||||
func (s *PackagePoolSuite) TestImportMove(c *C) {
|
||||
tmpDir := c.MkDir()
|
||||
tmpPath := filepath.Join(tmpDir, filepath.Base(s.debFile))
|
||||
|
||||
dst, err := os.Create(tmpPath)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
src, err := os.Open(s.debFile)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
_, err = io.Copy(dst, src)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
c.Assert(dst.Close(), IsNil)
|
||||
c.Assert(src.Close(), IsNil)
|
||||
|
||||
path, err := s.pool.Import(tmpPath, filepath.Base(tmpPath), &s.checksum, true, s.cs)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(path, Equals, "c7/6b/4bd12fd92e4dfe1b55b18a67a669_libboost-program-options-dev_1.49.0.1_i386.deb")
|
||||
|
||||
info, err := s.pool.Stat(path)
|
||||
c.Assert(err, IsNil)
|
||||
c.Check(info.Size(), Equals, int64(2738))
|
||||
c.Check(int(info.Sys().(*syscall.Stat_t).Nlink), Equals, 1)
|
||||
}
|
||||
|
||||
func (s *PackagePoolSuite) TestImportNotExist(c *C) {
|
||||
err := s.pool.Import("no-such-file", "91b1a1480b90b9e269ca44d897b12575")
|
||||
_, err := s.pool.Import("no-such-file", "a.deb", &s.checksum, false, s.cs)
|
||||
c.Check(err, ErrorMatches, ".*no such file or directory")
|
||||
}
|
||||
|
||||
func (s *PackagePoolSuite) TestImportOverwrite(c *C) {
|
||||
_, _File, _, _ := runtime.Caller(0)
|
||||
debFile := filepath.Join(filepath.Dir(_File), "../system/files/libboost-program-options-dev_1.49.0.1_i386.deb")
|
||||
os.MkdirAll(filepath.Join(s.pool.rootPath, "c7", "6b"), 0755)
|
||||
ioutil.WriteFile(filepath.Join(s.pool.rootPath, "c7", "6b", "4bd12fd92e4dfe1b55b18a67a669_libboost-program-options-dev_1.49.0.1_i386.deb"), []byte("1"), 0644)
|
||||
|
||||
os.MkdirAll(filepath.Join(s.pool.rootPath, "91", "b1"), 0755)
|
||||
ioutil.WriteFile(filepath.Join(s.pool.rootPath, "91", "b1", "libboost-program-options-dev_1.49.0.1_i386.deb"), []byte("1"), 0644)
|
||||
|
||||
err := s.pool.Import(debFile, "91b1a1480b90b9e269ca44d897b12575")
|
||||
_, err := s.pool.Import(s.debFile, filepath.Base(s.debFile), &s.checksum, false, s.cs)
|
||||
c.Check(err, ErrorMatches, "unable to import into pool.*")
|
||||
}
|
||||
|
||||
func (s *PackagePoolSuite) TestStat(c *C) {
|
||||
path, err := s.pool.Import(s.debFile, filepath.Base(s.debFile), &s.checksum, false, s.cs)
|
||||
c.Check(err, IsNil)
|
||||
|
||||
info, err := s.pool.Stat(path)
|
||||
c.Assert(err, IsNil)
|
||||
c.Check(info.Size(), Equals, int64(2738))
|
||||
|
||||
_, err = s.pool.Stat("do/es/ntexist")
|
||||
c.Assert(os.IsNotExist(err), Equals, true)
|
||||
}
|
||||
|
||||
func (s *PackagePoolSuite) TestOpen(c *C) {
|
||||
path, err := s.pool.Import(s.debFile, filepath.Base(s.debFile), &s.checksum, false, s.cs)
|
||||
c.Check(err, IsNil)
|
||||
|
||||
f, err := s.pool.Open(path)
|
||||
c.Assert(err, IsNil)
|
||||
contents, err := ioutil.ReadAll(f)
|
||||
c.Assert(err, IsNil)
|
||||
c.Check(len(contents), Equals, 2738)
|
||||
c.Check(f.Close(), IsNil)
|
||||
|
||||
_, err = s.pool.Open("do/es/ntexist")
|
||||
c.Assert(os.IsNotExist(err), Equals, true)
|
||||
}
|
||||
|
||||
func (s *PackagePoolSuite) TestLink(c *C) {
|
||||
path, err := s.pool.Import(s.debFile, filepath.Base(s.debFile), &s.checksum, false, s.cs)
|
||||
c.Check(err, IsNil)
|
||||
|
||||
tmpDir := c.MkDir()
|
||||
dstPath := filepath.Join(tmpDir, filepath.Base(s.debFile))
|
||||
c.Check(s.pool.Link(path, dstPath), IsNil)
|
||||
|
||||
info, err := os.Stat(dstPath)
|
||||
c.Assert(err, IsNil)
|
||||
c.Check(info.Size(), Equals, int64(2738))
|
||||
c.Check(info.Sys().(*syscall.Stat_t).Nlink > 2, Equals, true)
|
||||
}
|
||||
|
||||
func (s *PackagePoolSuite) TestSymlink(c *C) {
|
||||
path, err := s.pool.Import(s.debFile, filepath.Base(s.debFile), &s.checksum, false, s.cs)
|
||||
c.Check(err, IsNil)
|
||||
|
||||
tmpDir := c.MkDir()
|
||||
dstPath := filepath.Join(tmpDir, filepath.Base(s.debFile))
|
||||
c.Check(s.pool.Symlink(path, dstPath), IsNil)
|
||||
|
||||
info, err := os.Stat(dstPath)
|
||||
c.Assert(err, IsNil)
|
||||
c.Check(info.Size(), Equals, int64(2738))
|
||||
c.Check(info.Sys().(*syscall.Stat_t).Nlink > 2, Equals, true)
|
||||
|
||||
info, err = os.Lstat(dstPath)
|
||||
c.Assert(err, IsNil)
|
||||
c.Check(int(info.Sys().(*syscall.Stat_t).Mode&syscall.S_IFMT), Equals, int(syscall.S_IFLNK))
|
||||
}
|
||||
|
||||
func (s *PackagePoolSuite) TestGenerateRandomPath(c *C) {
|
||||
path, err := s.pool.GenerateTempPath("a.deb")
|
||||
c.Check(err, IsNil)
|
||||
|
||||
c.Check(path, Matches, ".+/[0-9a-f][0-9a-f]/[0-9a-f][0-9a-f]/[0-9a-f-]+a\\.deb")
|
||||
}
|
||||
|
||||
+111
-19
@@ -5,25 +5,62 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/smira/aptly/aptly"
|
||||
"github.com/smira/aptly/utils"
|
||||
)
|
||||
|
||||
// PublishedStorage abstract file system with public dirs (published repos)
|
||||
type PublishedStorage struct {
|
||||
rootPath string
|
||||
rootPath string
|
||||
linkMethod uint
|
||||
verifyMethod uint
|
||||
}
|
||||
|
||||
// Check interfaces
|
||||
var (
|
||||
_ aptly.PublishedStorage = (*PublishedStorage)(nil)
|
||||
_ aptly.LocalPublishedStorage = (*PublishedStorage)(nil)
|
||||
_ aptly.PublishedStorage = (*PublishedStorage)(nil)
|
||||
_ aptly.FileSystemPublishedStorage = (*PublishedStorage)(nil)
|
||||
)
|
||||
|
||||
// Constants defining the type of creating links
|
||||
const (
|
||||
LinkMethodHardLink uint = iota
|
||||
LinkMethodSymLink
|
||||
LinkMethodCopy
|
||||
)
|
||||
|
||||
// Constants defining the type of file verification for LinkMethodCopy
|
||||
const (
|
||||
VerificationMethodChecksum uint = iota
|
||||
VerificationMethodFileSize
|
||||
)
|
||||
|
||||
// NewPublishedStorage creates new instance of PublishedStorage which specified root
|
||||
func NewPublishedStorage(root string) *PublishedStorage {
|
||||
return &PublishedStorage{rootPath: filepath.Join(root, "public")}
|
||||
func NewPublishedStorage(root string, linkMethod string, verifyMethod string) *PublishedStorage {
|
||||
// Ensure linkMethod is one of 'hardlink', 'symlink', 'copy'
|
||||
var verifiedLinkMethod uint
|
||||
|
||||
if strings.EqualFold(linkMethod, "copy") {
|
||||
verifiedLinkMethod = LinkMethodCopy
|
||||
} else if strings.EqualFold(linkMethod, "symlink") {
|
||||
verifiedLinkMethod = LinkMethodSymLink
|
||||
} else {
|
||||
verifiedLinkMethod = LinkMethodHardLink
|
||||
}
|
||||
|
||||
var verifiedVerifyMethod uint
|
||||
|
||||
if strings.EqualFold(verifyMethod, "size") {
|
||||
verifiedVerifyMethod = VerificationMethodFileSize
|
||||
} else {
|
||||
verifiedVerifyMethod = VerificationMethodChecksum
|
||||
}
|
||||
|
||||
return &PublishedStorage{rootPath: root, linkMethod: verifiedLinkMethod,
|
||||
verifyMethod: verifiedVerifyMethod}
|
||||
}
|
||||
|
||||
// PublicPath returns root of public part
|
||||
@@ -77,15 +114,12 @@ func (storage *PublishedStorage) RemoveDirs(path string, progress aptly.Progress
|
||||
//
|
||||
// publishedDirectory is desired location in pool (like prefix/pool/component/liba/libav/)
|
||||
// sourcePool is instance of aptly.PackagePool
|
||||
// sourcePath is filepath to package file in package pool
|
||||
// sourcePath is a relative path to package file in package pool
|
||||
//
|
||||
// LinkFromPool returns relative path for the published file to be included in package index
|
||||
func (storage *PublishedStorage) LinkFromPool(publishedDirectory string, sourcePool aptly.PackagePool,
|
||||
sourcePath, sourceMD5 string, force bool) error {
|
||||
// verify that package pool is local pool is filesystem pool
|
||||
_ = sourcePool.(*PackagePool)
|
||||
func (storage *PublishedStorage) LinkFromPool(publishedDirectory, baseName string, sourcePool aptly.PackagePool,
|
||||
sourcePath string, sourceChecksums utils.ChecksumInfo, force bool) error {
|
||||
|
||||
baseName := filepath.Base(sourcePath)
|
||||
poolPath := filepath.Join(storage.rootPath, publishedDirectory)
|
||||
|
||||
err := os.MkdirAll(poolPath, 0777)
|
||||
@@ -98,18 +132,42 @@ func (storage *PublishedStorage) LinkFromPool(publishedDirectory string, sourceP
|
||||
dstStat, err = os.Stat(filepath.Join(poolPath, baseName))
|
||||
if err == nil {
|
||||
// already exists, check source file
|
||||
srcStat, err = os.Stat(sourcePath)
|
||||
srcStat, err = sourcePool.Stat(sourcePath)
|
||||
if err != nil {
|
||||
// source file doesn't exist? problem!
|
||||
return err
|
||||
}
|
||||
|
||||
srcSys := srcStat.Sys().(*syscall.Stat_t)
|
||||
dstSys := dstStat.Sys().(*syscall.Stat_t)
|
||||
if storage.linkMethod == LinkMethodCopy {
|
||||
if storage.verifyMethod == VerificationMethodFileSize {
|
||||
// if source and destination have the same size, no need to copy
|
||||
if srcStat.Size() == dstStat.Size() {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
// if source and destination have the same checksums, no need to copy
|
||||
var dstMD5 string
|
||||
dstMD5, err = utils.MD5ChecksumForFile(filepath.Join(poolPath, baseName))
|
||||
|
||||
// source and destination inodes match, no need to link
|
||||
if srcSys.Ino == dstSys.Ino {
|
||||
return nil
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if dstMD5 == sourceChecksums.MD5 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
srcSys := srcStat.Sys().(*syscall.Stat_t)
|
||||
dstSys := dstStat.Sys().(*syscall.Stat_t)
|
||||
|
||||
// if source and destination inodes match, no need to link
|
||||
|
||||
// Symlink can point to different filesystem with identical inodes
|
||||
// so we have to check the device as well.
|
||||
if srcSys.Ino == dstSys.Ino && srcSys.Dev == dstSys.Dev {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// source and destination have different inodes, if !forced, this is fatal error
|
||||
@@ -124,8 +182,42 @@ func (storage *PublishedStorage) LinkFromPool(publishedDirectory string, sourceP
|
||||
}
|
||||
}
|
||||
|
||||
// destination doesn't exist (or forced), create link
|
||||
return os.Link(sourcePath, filepath.Join(poolPath, baseName))
|
||||
// destination doesn't exist (or forced), create link or copy
|
||||
if storage.linkMethod == LinkMethodCopy {
|
||||
var r aptly.ReadSeekerCloser
|
||||
r, err = sourcePool.Open(sourcePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var dst *os.File
|
||||
dst, err = os.Create(filepath.Join(poolPath, baseName))
|
||||
if err != nil {
|
||||
r.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(dst, r)
|
||||
if err != nil {
|
||||
r.Close()
|
||||
dst.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
err = r.Close()
|
||||
if err != nil {
|
||||
dst.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
err = dst.Close()
|
||||
} else if storage.linkMethod == LinkMethodSymLink {
|
||||
err = sourcePool.(aptly.LocalPackagePool).Symlink(sourcePath, filepath.Join(poolPath, baseName))
|
||||
} else {
|
||||
err = sourcePool.(aptly.LocalPackagePool).Link(sourcePath, filepath.Join(poolPath, baseName))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Filelist returns list of files under prefix
|
||||
|
||||
+110
-27
@@ -6,23 +6,49 @@ import (
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/smira/aptly/aptly"
|
||||
"github.com/smira/aptly/utils"
|
||||
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
type PublishedStorageSuite struct {
|
||||
root string
|
||||
storage *PublishedStorage
|
||||
root string
|
||||
storage *PublishedStorage
|
||||
storageSymlink *PublishedStorage
|
||||
storageCopy *PublishedStorage
|
||||
storageCopySize *PublishedStorage
|
||||
cs aptly.ChecksumStorage
|
||||
}
|
||||
|
||||
var _ = Suite(&PublishedStorageSuite{})
|
||||
|
||||
func (s *PublishedStorageSuite) SetUpTest(c *C) {
|
||||
s.root = c.MkDir()
|
||||
s.storage = NewPublishedStorage(s.root)
|
||||
s.storage = NewPublishedStorage(filepath.Join(s.root, "public"), "", "")
|
||||
s.storageSymlink = NewPublishedStorage(filepath.Join(s.root, "public_symlink"), "symlink", "")
|
||||
s.storageCopy = NewPublishedStorage(filepath.Join(s.root, "public_copy"), "copy", "")
|
||||
s.storageCopySize = NewPublishedStorage(filepath.Join(s.root, "public_copysize"), "copy", "size")
|
||||
s.cs = NewMockChecksumStorage()
|
||||
}
|
||||
|
||||
func (s *PublishedStorageSuite) TestLinkMethodField(c *C) {
|
||||
c.Assert(s.storage.linkMethod, Equals, LinkMethodHardLink)
|
||||
c.Assert(s.storageSymlink.linkMethod, Equals, LinkMethodSymLink)
|
||||
c.Assert(s.storageCopy.linkMethod, Equals, LinkMethodCopy)
|
||||
c.Assert(s.storageCopySize.linkMethod, Equals, LinkMethodCopy)
|
||||
}
|
||||
|
||||
func (s *PublishedStorageSuite) TestVerifyMethodField(c *C) {
|
||||
c.Assert(s.storageCopy.verifyMethod, Equals, VerificationMethodChecksum)
|
||||
c.Assert(s.storageCopySize.verifyMethod, Equals, VerificationMethodFileSize)
|
||||
}
|
||||
|
||||
func (s *PublishedStorageSuite) TestPublicPath(c *C) {
|
||||
c.Assert(s.storage.PublicPath(), Equals, filepath.Join(s.root, "public"))
|
||||
c.Assert(s.storageSymlink.PublicPath(), Equals, filepath.Join(s.root, "public_symlink"))
|
||||
c.Assert(s.storageCopy.PublicPath(), Equals, filepath.Join(s.root, "public_copy"))
|
||||
c.Assert(s.storageCopySize.PublicPath(), Equals, filepath.Join(s.root, "public_copysize"))
|
||||
}
|
||||
|
||||
func (s *PublishedStorageSuite) TestMkDir(c *C) {
|
||||
@@ -33,7 +59,7 @@ func (s *PublishedStorageSuite) TestMkDir(c *C) {
|
||||
c.Assert(err, IsNil)
|
||||
}
|
||||
|
||||
func (s *PublishedStorageSuite) TesPutFile(c *C) {
|
||||
func (s *PublishedStorageSuite) TestPutFile(c *C) {
|
||||
err := s.storage.MkDir("ppa/dists/squeeze/")
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
@@ -85,6 +111,7 @@ func (s *PublishedStorageSuite) TestRemoveDirs(c *C) {
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = s.storage.RemoveDirs("ppa/dists/", nil)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
_, err = os.Stat(filepath.Join(s.storage.rootPath, "ppa/dists/squeeze/Release"))
|
||||
c.Assert(err, NotNil)
|
||||
@@ -99,6 +126,7 @@ func (s *PublishedStorageSuite) TestRemove(c *C) {
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = s.storage.Remove("ppa/dists/squeeze/Release")
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
_, err = os.Stat(filepath.Join(s.storage.rootPath, "ppa/dists/squeeze/Release"))
|
||||
c.Assert(err, NotNil)
|
||||
@@ -116,78 +144,133 @@ func (s *PublishedStorageSuite) TestLinkFromPool(c *C) {
|
||||
{ // package name regular
|
||||
prefix: "",
|
||||
component: "main",
|
||||
sourcePath: "pool/01/ae/mars-invaders_1.03.deb",
|
||||
sourcePath: "mars-invaders_1.03.deb",
|
||||
poolDirectory: "m/mars-invaders",
|
||||
expectedFilename: "pool/main/m/mars-invaders/mars-invaders_1.03.deb",
|
||||
},
|
||||
{ // lib-like filename
|
||||
prefix: "",
|
||||
component: "main",
|
||||
sourcePath: "pool/01/ae/libmars-invaders_1.03.deb",
|
||||
sourcePath: "libmars-invaders_1.03.deb",
|
||||
poolDirectory: "libm/libmars-invaders",
|
||||
expectedFilename: "pool/main/libm/libmars-invaders/libmars-invaders_1.03.deb",
|
||||
},
|
||||
{ // duplicate link, shouldn't panic
|
||||
prefix: "",
|
||||
component: "main",
|
||||
sourcePath: "pool/01/ae/mars-invaders_1.03.deb",
|
||||
sourcePath: "mars-invaders_1.03.deb",
|
||||
poolDirectory: "m/mars-invaders",
|
||||
expectedFilename: "pool/main/m/mars-invaders/mars-invaders_1.03.deb",
|
||||
},
|
||||
{ // prefix & component
|
||||
prefix: "ppa",
|
||||
component: "contrib",
|
||||
sourcePath: "pool/01/ae/libmars-invaders_1.04.deb",
|
||||
sourcePath: "libmars-invaders_1.04.deb",
|
||||
poolDirectory: "libm/libmars-invaders",
|
||||
expectedFilename: "pool/contrib/libm/libmars-invaders/libmars-invaders_1.04.deb",
|
||||
},
|
||||
}
|
||||
|
||||
pool := NewPackagePool(s.root)
|
||||
pool := NewPackagePool(s.root, false)
|
||||
|
||||
for _, t := range tests {
|
||||
t.sourcePath = filepath.Join(s.root, t.sourcePath)
|
||||
|
||||
err := os.MkdirAll(filepath.Dir(t.sourcePath), 0755)
|
||||
tmpPath := filepath.Join(c.MkDir(), t.sourcePath)
|
||||
err := ioutil.WriteFile(tmpPath, []byte("Contents"), 0644)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = ioutil.WriteFile(t.sourcePath, []byte("Contents"), 0644)
|
||||
sourceChecksum, err := utils.ChecksumsForFile(tmpPath)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = s.storage.LinkFromPool(filepath.Join(t.prefix, "pool", t.component, t.poolDirectory), pool, t.sourcePath, "", false)
|
||||
srcPoolPath, err := pool.Import(tmpPath, t.sourcePath, &utils.ChecksumInfo{MD5: "c1df1da7a1ce305a3b60af9d5733ac1d"}, false, s.cs)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// Test using hardlinks
|
||||
err = s.storage.LinkFromPool(filepath.Join(t.prefix, "pool", t.component, t.poolDirectory), t.sourcePath, pool, srcPoolPath, sourceChecksum, false)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
st, err := os.Stat(filepath.Join(s.storage.rootPath, t.prefix, t.expectedFilename))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
info := st.Sys().(*syscall.Stat_t)
|
||||
c.Check(int(info.Nlink), Equals, 2)
|
||||
c.Check(int(info.Nlink), Equals, 3)
|
||||
|
||||
// Test using symlinks
|
||||
err = s.storageSymlink.LinkFromPool(filepath.Join(t.prefix, "pool", t.component, t.poolDirectory), t.sourcePath, pool, srcPoolPath, sourceChecksum, false)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
st, err = os.Lstat(filepath.Join(s.storageSymlink.rootPath, t.prefix, t.expectedFilename))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
info = st.Sys().(*syscall.Stat_t)
|
||||
c.Check(int(info.Nlink), Equals, 1)
|
||||
c.Check(int(info.Mode&syscall.S_IFMT), Equals, int(syscall.S_IFLNK))
|
||||
|
||||
// Test using copy with checksum verification
|
||||
err = s.storageCopy.LinkFromPool(filepath.Join(t.prefix, "pool", t.component, t.poolDirectory), t.sourcePath, pool, srcPoolPath, sourceChecksum, false)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
st, err = os.Stat(filepath.Join(s.storageCopy.rootPath, t.prefix, t.expectedFilename))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
info = st.Sys().(*syscall.Stat_t)
|
||||
c.Check(int(info.Nlink), Equals, 1)
|
||||
|
||||
// Test using copy with size verification
|
||||
err = s.storageCopySize.LinkFromPool(filepath.Join(t.prefix, "pool", t.component, t.poolDirectory), t.sourcePath, pool, srcPoolPath, sourceChecksum, false)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
st, err = os.Stat(filepath.Join(s.storageCopySize.rootPath, t.prefix, t.expectedFilename))
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
info = st.Sys().(*syscall.Stat_t)
|
||||
c.Check(int(info.Nlink), Equals, 1)
|
||||
}
|
||||
|
||||
// test linking files to duplicate final name
|
||||
sourcePath := filepath.Join(s.root, "pool/02/bc/mars-invaders_1.03.deb")
|
||||
err := os.MkdirAll(filepath.Dir(sourcePath), 0755)
|
||||
tmpPath := filepath.Join(c.MkDir(), "mars-invaders_1.03.deb")
|
||||
err := ioutil.WriteFile(tmpPath, []byte("cONTENTS"), 0644)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = ioutil.WriteFile(sourcePath, []byte("Contents"), 0644)
|
||||
sourceChecksum, err := utils.ChecksumsForFile(tmpPath)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
err = s.storage.LinkFromPool(filepath.Join("", "pool", "main", "m/mars-invaders"), pool, sourcePath, "", false)
|
||||
srcPoolPath, err := pool.Import(tmpPath, "mars-invaders_1.03.deb", &utils.ChecksumInfo{MD5: "02bcda7a1ce305a3b60af9d5733ac1d"}, true, s.cs)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
st, err := pool.Stat(srcPoolPath)
|
||||
c.Assert(err, IsNil)
|
||||
nlinks := int(st.Sys().(*syscall.Stat_t).Nlink)
|
||||
|
||||
err = s.storage.LinkFromPool(filepath.Join("", "pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, srcPoolPath, sourceChecksum, false)
|
||||
c.Check(err, ErrorMatches, ".*file already exists and is different")
|
||||
|
||||
st, err := os.Stat(sourcePath)
|
||||
st, err = pool.Stat(srcPoolPath)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
info := st.Sys().(*syscall.Stat_t)
|
||||
c.Check(int(info.Nlink), Equals, 1)
|
||||
c.Check(int(st.Sys().(*syscall.Stat_t).Nlink), Equals, nlinks)
|
||||
|
||||
// linking with force
|
||||
err = s.storage.LinkFromPool(filepath.Join("", "pool", "main", "m/mars-invaders"), pool, sourcePath, "", true)
|
||||
err = s.storage.LinkFromPool(filepath.Join("", "pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, srcPoolPath, sourceChecksum, true)
|
||||
c.Check(err, IsNil)
|
||||
|
||||
st, err = os.Stat(sourcePath)
|
||||
st, err = pool.Stat(srcPoolPath)
|
||||
c.Assert(err, IsNil)
|
||||
c.Check(int(st.Sys().(*syscall.Stat_t).Nlink), Equals, nlinks+1)
|
||||
|
||||
info = st.Sys().(*syscall.Stat_t)
|
||||
c.Check(int(info.Nlink), Equals, 2)
|
||||
// Test using symlinks
|
||||
err = s.storageSymlink.LinkFromPool(filepath.Join("", "pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, srcPoolPath, sourceChecksum, false)
|
||||
c.Check(err, ErrorMatches, ".*file already exists and is different")
|
||||
|
||||
err = s.storageSymlink.LinkFromPool(filepath.Join("", "pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, srcPoolPath, sourceChecksum, true)
|
||||
c.Check(err, IsNil)
|
||||
|
||||
// Test using copy with checksum verification
|
||||
err = s.storageCopy.LinkFromPool(filepath.Join("", "pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, srcPoolPath, sourceChecksum, false)
|
||||
c.Check(err, ErrorMatches, ".*file already exists and is different")
|
||||
|
||||
err = s.storageCopy.LinkFromPool(filepath.Join("", "pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, srcPoolPath, sourceChecksum, true)
|
||||
c.Check(err, IsNil)
|
||||
|
||||
// Test using copy with size verification (this will NOT detect the difference)
|
||||
err = s.storageCopySize.LinkFromPool(filepath.Join("", "pool", "main", "m/mars-invaders"), "mars-invaders_1.03.deb", pool, srcPoolPath, sourceChecksum, false)
|
||||
c.Check(err, IsNil)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user