diff --git a/AUTHORS b/AUTHORS index 00bc4557ac..160ee7bb24 100644 --- a/AUTHORS +++ b/AUTHORS @@ -49,3 +49,4 @@ List of contributors, in chronological order: * Samuel Mutel (https://github.com/smutel) * Russell Greene (https://github.com/russelltg) * Wade Simmons (https://github.com/wadey) +* Tuan Lee (https://github.com/vleedev) diff --git a/aptly/interfaces.go b/aptly/interfaces.go index e912daba2b..a0a2b274eb 100644 --- a/aptly/interfaces.go +++ b/aptly/interfaces.go @@ -52,7 +52,7 @@ type LocalPackagePool interface { // Link generates hardlink to destination path Link(path, dstPath string) error // Symlink generates symlink to destination path - Symlink(path, dstPath string) error + Symlink(path, dstPath string, makeRelative bool) error // FullPath generates full path to the file in pool // // Please use with care: it's not supposed to be used to access files diff --git a/files/package_pool.go b/files/package_pool.go index 8b1e8b252e..92a26ba57a 100644 --- a/files/package_pool.go +++ b/files/package_pool.go @@ -394,8 +394,20 @@ func (pool *PackagePool) Link(path, dstPath string) error { } // Symlink generates symlink to destination path -func (pool *PackagePool) Symlink(path, dstPath string) error { - return os.Symlink(filepath.Join(pool.rootPath, path), dstPath) +func (pool *PackagePool) Symlink(path, dstPath string, makeRelative bool) error { + // Convert path to absolute path + path = filepath.Join(pool.rootPath, path) + if makeRelative { + // Take dir of dst + dstDir := filepath.Dir(dstPath) + // Take relative path to go from path to destination directory + relativePath, err := filepath.Rel(dstDir, path) + if err != nil { + return err + } + path = relativePath + } + return os.Symlink(path, dstPath) } // FullPath generates full path to the file in pool diff --git a/files/package_pool_test.go b/files/package_pool_test.go index 3f65ed2c89..0648244aaa 100644 --- a/files/package_pool_test.go +++ b/files/package_pool_test.go @@ -352,25 +352,28 @@ func (s *PackagePoolSuite) TestLink(c *C) { } 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)) - if isSameDevice(s) { - c.Check(info.Sys().(*syscall.Stat_t).Nlink > 2, Equals, true) - } else { - c.Check(info.Sys().(*syscall.Stat_t).Nlink, Equals, uint64(1)) + // Test absolute symlink: makeRelative = false + // Test relative symlink: makeRelative = true + for _, makeRelative := range []bool{false, true} { + 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, makeRelative), IsNil) + info, err := os.Stat(dstPath) + c.Assert(err, IsNil) + c.Check(info.Size(), Equals, int64(2738)) + if isSameDevice(s) { + c.Check(info.Sys().(*syscall.Stat_t).Nlink > 2, Equals, true) + } else { + c.Check(info.Sys().(*syscall.Stat_t).Nlink, Equals, uint64(1)) + } + + 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)) } - - 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) { diff --git a/files/public.go b/files/public.go index fc87b4104e..1e5d80f58b 100644 --- a/files/public.go +++ b/files/public.go @@ -28,7 +28,8 @@ var ( // Constants defining the type of creating links const ( LinkMethodHardLink uint = iota - LinkMethodSymLink + LinkMethodAbsoluteSymLink + LinkMethodRelativeSymLink LinkMethodCopy ) @@ -45,8 +46,11 @@ func NewPublishedStorage(root string, linkMethod string, verifyMethod string) *P if strings.EqualFold(linkMethod, "copy") { verifiedLinkMethod = LinkMethodCopy - } else if strings.EqualFold(linkMethod, "symlink") { - verifiedLinkMethod = LinkMethodSymLink + } else if strings.EqualFold(linkMethod, "symlink") || strings.EqualFold(linkMethod, "absoluteSymlink") { + // By default: symlink is absolute symlink + verifiedLinkMethod = LinkMethodAbsoluteSymLink + } else if strings.EqualFold(linkMethod, "relativeSymlink") { + verifiedLinkMethod = LinkMethodRelativeSymLink } else { verifiedLinkMethod = LinkMethodHardLink } @@ -218,8 +222,10 @@ func (storage *PublishedStorage) LinkFromPool(publishedDirectory, fileName strin } err = dst.Close() - } else if storage.linkMethod == LinkMethodSymLink { - err = sourcePool.(aptly.LocalPackagePool).Symlink(sourcePath, filepath.Join(poolPath, baseName)) + } else if storage.linkMethod == LinkMethodAbsoluteSymLink { + err = sourcePool.(aptly.LocalPackagePool).Symlink(sourcePath, filepath.Join(poolPath, baseName), false) + } else if storage.linkMethod == LinkMethodRelativeSymLink { + err = sourcePool.(aptly.LocalPackagePool).Symlink(sourcePath, filepath.Join(poolPath, baseName), true) } else { err = sourcePool.(aptly.LocalPackagePool).Link(sourcePath, filepath.Join(poolPath, baseName)) } diff --git a/files/public_test.go b/files/public_test.go index 16f724cb7a..84fd944f60 100644 --- a/files/public_test.go +++ b/files/public_test.go @@ -13,12 +13,14 @@ import ( ) type PublishedStorageSuite struct { - root string - storage *PublishedStorage - storageSymlink *PublishedStorage - storageCopy *PublishedStorage - storageCopySize *PublishedStorage - cs aptly.ChecksumStorage + root string + storage *PublishedStorage + storageSymlink *PublishedStorage + storageAbsoluteSymlink *PublishedStorage + storageRelativeSymlink *PublishedStorage + storageCopy *PublishedStorage + storageCopySize *PublishedStorage + cs aptly.ChecksumStorage } var _ = Suite(&PublishedStorageSuite{}) @@ -27,6 +29,8 @@ func (s *PublishedStorageSuite) SetUpTest(c *C) { s.root = c.MkDir() s.storage = NewPublishedStorage(filepath.Join(s.root, "public"), "", "") s.storageSymlink = NewPublishedStorage(filepath.Join(s.root, "public_symlink"), "symlink", "") + s.storageAbsoluteSymlink = NewPublishedStorage(filepath.Join(s.root, "public_absolute_symlink"), "absoluteSymlink", "") + s.storageRelativeSymlink = NewPublishedStorage(filepath.Join(s.root, "public_relative_symlink"), "relativeSymlink", "") s.storageCopy = NewPublishedStorage(filepath.Join(s.root, "public_copy"), "copy", "") s.storageCopySize = NewPublishedStorage(filepath.Join(s.root, "public_copysize"), "copy", "size") s.cs = NewMockChecksumStorage() @@ -34,7 +38,9 @@ func (s *PublishedStorageSuite) SetUpTest(c *C) { func (s *PublishedStorageSuite) TestLinkMethodField(c *C) { c.Assert(s.storage.linkMethod, Equals, LinkMethodHardLink) - c.Assert(s.storageSymlink.linkMethod, Equals, LinkMethodSymLink) + c.Assert(s.storageSymlink.linkMethod, Equals, LinkMethodAbsoluteSymLink) + c.Assert(s.storageAbsoluteSymlink.linkMethod, Equals, LinkMethodAbsoluteSymLink) + c.Assert(s.storageRelativeSymlink.linkMethod, Equals, LinkMethodRelativeSymLink) c.Assert(s.storageCopy.linkMethod, Equals, LinkMethodCopy) c.Assert(s.storageCopySize.linkMethod, Equals, LinkMethodCopy) } @@ -47,6 +53,8 @@ func (s *PublishedStorageSuite) TestVerifyMethodField(c *C) { 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.storageAbsoluteSymlink.PublicPath(), Equals, filepath.Join(s.root, "public_absolute_symlink")) + c.Assert(s.storageRelativeSymlink.PublicPath(), Equals, filepath.Join(s.root, "public_relative_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")) } diff --git a/man/aptly.1 b/man/aptly.1 index 4cc042a4ae..b724d38a23 100644 --- a/man/aptly.1 +++ b/man/aptly.1 @@ -62,11 +62,19 @@ Configuration file is stored in JSON format (default values shown below): }, "test2": { "rootDir": "/opt/srv2/aptly_public", - "linkMethod": "copy", - "verifyMethod": "md5" + "linkMethod": "absoluteSymlink" }, "test3": { "rootDir": "/opt/srv3/aptly_public", + "linkMethod": "relativeSymlink" + }, + "test4": { + "rootDir": "/opt/srv4/aptly_public", + "linkMethod": "copy", + "verifyMethod": "md5" + }, + "test5": { + "rootDir": "/opt/srv5/aptly_public", "linkMethod": "hardlink" } }, @@ -205,7 +213,7 @@ The publish directory, e\.g\., \fB/opt/srv/aptly_public\fR\. . .TP \fBlinkMethod\fR -This is one of \fBhardlink\fR, \fBsymlink\fR or \fBcopy\fR\. It specifies how aptly links the files from the internal pool to the published directory\. If not specified, empty or wrong, this defaults to \fBhardlink\fR\. +This is one of \fBhardlink\fR, \fBsymlink\fR, \fBabsoluteSymlink\fR, \fBrelativeSymlink\fR or \fBcopy\fR\. It specifies how aptly links the files from the internal pool to the published directory\. The option \fBsymlink\fR will be treated as \fBabsoluteSymlink\fR\. If not specified, empty or wrong, this defaults to \fBhardlink\fR\. . .TP \fBverifyMethod\fR diff --git a/man/aptly.1.ronn.tmpl b/man/aptly.1.ronn.tmpl index 82cc1dfc5c..aa36dc2a28 100644 --- a/man/aptly.1.ronn.tmpl +++ b/man/aptly.1.ronn.tmpl @@ -54,11 +54,19 @@ Configuration file is stored in JSON format (default values shown below): }, "test2": { "rootDir": "/opt/srv2/aptly_public", - "linkMethod": "copy", - "verifyMethod": "md5" + "linkMethod": "absoluteSymlink" }, "test3": { "rootDir": "/opt/srv3/aptly_public", + "linkMethod": "relativeSymlink" + }, + "test4": { + "rootDir": "/opt/srv4/aptly_public", + "linkMethod": "copy", + "verifyMethod": "md5" + }, + "test5": { + "rootDir": "/opt/srv5/aptly_public", "linkMethod": "hardlink" } }, @@ -187,8 +195,9 @@ and the following associated settings: * `rootDir`: The publish directory, e.g., `/opt/srv/aptly_public`. * `linkMethod`: - This is one of `hardlink`, `symlink` or `copy`. It specifies how aptly links the - files from the internal pool to the published directory. + This is one of `hardlink`, `symlink`, `absoluteSymlink`, `relativeSymlink` or `copy`. + It specifies how aptly links the files from the internal pool to the published directory. + The option `symlink` will be treated as `absoluteSymlink`. If not specified, empty or wrong, this defaults to `hardlink`. * `verifyMethod`: This is used only when setting the `linkMethod` to `copy`. Possible values are