mirror of
https://github.com/GoogleContainerTools/kaniko
synced 2026-10-18 04:46:31 +02:00
Updated vendor, for container-diff
This commit is contained in:
+31
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -20,13 +20,14 @@ import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
img "github.com/containers/image/image"
|
||||
"github.com/containers/image/types"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containers/image/manifest"
|
||||
"github.com/containers/image/types"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
@@ -76,28 +77,46 @@ func (m *MutableSource) GetManifest(_ *digest.Digest) ([]byte, string, error) {
|
||||
|
||||
// populateManifestAndConfig parses the raw manifest and configs, storing them on the struct.
|
||||
func (m *MutableSource) populateManifestAndConfig() error {
|
||||
mfstBytes, _, err := m.ProxySource.GetManifest(nil)
|
||||
context := &types.SystemContext{
|
||||
OSChoice: "linux",
|
||||
ArchitectureChoice: "amd64",
|
||||
}
|
||||
image, err := m.ProxySource.Ref.NewImage(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer image.Close()
|
||||
// First get manifest
|
||||
mfstBytes, mfstType, err := image.Manifest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mfstType == manifest.DockerV2ListMediaType {
|
||||
// We need to select a manifest digest from the manifest list
|
||||
unparsedImage := img.UnparsedInstance(m.ImageSource, nil)
|
||||
|
||||
mfstDigest, err := img.ChooseManifestInstanceFromManifestList(context, unparsedImage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mfstBytes, _, err = m.ProxySource.GetManifest(&mfstDigest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
m.mfst, err = manifest.Schema2FromManifest(mfstBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bi := types.BlobInfo{Digest: m.mfst.ConfigDescriptor.Digest}
|
||||
r, _, err := m.GetBlob(bi)
|
||||
// Now, get config
|
||||
configBlob, err := image.ConfigBlob()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfgBytes, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal(cfgBytes, &m.cfg)
|
||||
return json.Unmarshal(configBlob, &m.cfg)
|
||||
}
|
||||
|
||||
// GetBlob first checks the stored "extra" blobs, then proxies the call to the original source.
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -32,6 +32,7 @@ func NewProxySource(ref types.ImageReference) (*ProxySource, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer src.Close()
|
||||
img, err := ref.NewImage(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
+10
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -32,21 +32,25 @@ type CloudPrepper struct {
|
||||
ImageSource types.ImageSource
|
||||
}
|
||||
|
||||
func (p CloudPrepper) Name() string {
|
||||
func (p *CloudPrepper) Name() string {
|
||||
return "Cloud Registry"
|
||||
}
|
||||
|
||||
func (p CloudPrepper) GetSource() string {
|
||||
func (p *CloudPrepper) GetSource() string {
|
||||
return p.Source
|
||||
}
|
||||
|
||||
func (p CloudPrepper) GetImage() (Image, error) {
|
||||
func (p *CloudPrepper) SetSource(source string) {
|
||||
p.Source = source
|
||||
}
|
||||
|
||||
func (p *CloudPrepper) GetImage() (Image, error) {
|
||||
image, err := getImage(p)
|
||||
image.Type = ImageTypeCloud
|
||||
return image, err
|
||||
}
|
||||
|
||||
func (p CloudPrepper) GetFileSystem() (string, error) {
|
||||
func (p *CloudPrepper) GetFileSystem() (string, error) {
|
||||
ref, err := docker.ParseReference("//" + p.Source)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -62,7 +66,7 @@ func (p CloudPrepper) GetFileSystem() (string, error) {
|
||||
return path, GetFileSystemFromReference(ref, p.ImageSource, path, nil)
|
||||
}
|
||||
|
||||
func (p CloudPrepper) GetConfig() (ConfigSchema, error) {
|
||||
func (p *CloudPrepper) GetConfig() (ConfigSchema, error) {
|
||||
ref, err := docker.ParseReference("//" + p.Source)
|
||||
if err != nil {
|
||||
return ConfigSchema{}, err
|
||||
|
||||
+12
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -32,21 +32,25 @@ type DaemonPrepper struct {
|
||||
Client *client.Client
|
||||
}
|
||||
|
||||
func (p DaemonPrepper) Name() string {
|
||||
func (p *DaemonPrepper) Name() string {
|
||||
return "Local Daemon"
|
||||
}
|
||||
|
||||
func (p DaemonPrepper) GetSource() string {
|
||||
func (p *DaemonPrepper) GetSource() string {
|
||||
return p.Source
|
||||
}
|
||||
|
||||
func (p DaemonPrepper) GetImage() (Image, error) {
|
||||
func (p *DaemonPrepper) SetSource(source string) {
|
||||
p.Source = source
|
||||
}
|
||||
|
||||
func (p *DaemonPrepper) GetImage() (Image, error) {
|
||||
image, err := getImage(p)
|
||||
image.Type = ImageTypeDaemon
|
||||
return image, err
|
||||
}
|
||||
|
||||
func (p DaemonPrepper) GetFileSystem() (string, error) {
|
||||
func (p *DaemonPrepper) GetFileSystem() (string, error) {
|
||||
ref, err := daemon.ParseReference(p.Source)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -56,6 +60,7 @@ func (p DaemonPrepper) GetFileSystem() (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
sanitizedName := strings.Replace(p.Source, ":", "", -1)
|
||||
sanitizedName = strings.Replace(sanitizedName, "/", "", -1)
|
||||
@@ -67,7 +72,7 @@ func (p DaemonPrepper) GetFileSystem() (string, error) {
|
||||
return path, GetFileSystemFromReference(ref, src, path, nil)
|
||||
}
|
||||
|
||||
func (p DaemonPrepper) GetConfig() (ConfigSchema, error) {
|
||||
func (p *DaemonPrepper) GetConfig() (ConfigSchema, error) {
|
||||
ref, err := daemon.ParseReference(p.Source)
|
||||
if err != nil {
|
||||
return ConfigSchema{}, err
|
||||
@@ -75,7 +80,7 @@ func (p DaemonPrepper) GetConfig() (ConfigSchema, error) {
|
||||
return getConfigFromReference(ref, p.Source)
|
||||
}
|
||||
|
||||
func (p DaemonPrepper) GetHistory() []ImageHistoryItem {
|
||||
func (p *DaemonPrepper) GetHistory() []ImageHistoryItem {
|
||||
history, err := p.Client.ImageHistory(context.Background(), p.Source)
|
||||
if err != nil {
|
||||
logrus.Errorf("Could not obtain image history for %s: %s", p.Source, err)
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
Generated
Vendored
+83
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -20,8 +20,10 @@ import (
|
||||
"archive/tar"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/GoogleCloudPlatform/container-diff/cmd/util/output"
|
||||
@@ -38,6 +40,7 @@ type Prepper interface {
|
||||
GetFileSystem() (string, error)
|
||||
GetImage() (Image, error)
|
||||
GetSource() string
|
||||
SetSource(string)
|
||||
}
|
||||
|
||||
type ImageType int
|
||||
@@ -84,13 +87,60 @@ type ImageHistoryItem struct {
|
||||
}
|
||||
|
||||
type ConfigObject struct {
|
||||
Env []string `json:"Env"`
|
||||
Entrypoint []string `json:"Entrypoint"`
|
||||
Hostname string
|
||||
Domainname string
|
||||
User string
|
||||
AttachStdin bool
|
||||
AttachStdout bool
|
||||
AttachStderr bool
|
||||
ExposedPorts map[string]struct{} `json:"ExposedPorts"`
|
||||
Cmd []string `json:"Cmd"`
|
||||
Volumes map[string]struct{} `json:"Volumes"`
|
||||
Workdir string `json:"WorkingDir"`
|
||||
Labels map[string]string `json:"Labels"`
|
||||
Tty bool
|
||||
OpenStdin bool
|
||||
StdinOnce bool
|
||||
Env []string `json:"Env"`
|
||||
Cmd []string `json:"Cmd"`
|
||||
// Healthcheck *HealthConfig
|
||||
ArgsEscaped bool `json:",omitempty"`
|
||||
Image string
|
||||
Volumes map[string]struct{} `json:"Volumes"`
|
||||
Workdir string `json:"WorkingDir"`
|
||||
Entrypoint []string `json:"Entrypoint"`
|
||||
NetworkDisabled bool `json:",omitempty"`
|
||||
MacAddress string `json:",omitempty"`
|
||||
OnBuild []string
|
||||
Labels map[string]string `json:"Labels"`
|
||||
StopSignal string `json:",omitempty"`
|
||||
StopTimeout *int `json:",omitempty"`
|
||||
Shell []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (c ConfigObject) AsList() []string {
|
||||
return []string{
|
||||
fmt.Sprintf("Hostname: %s", c.Hostname),
|
||||
fmt.Sprintf("Domainname: %s", c.Domainname),
|
||||
fmt.Sprintf("User: %s", c.User),
|
||||
fmt.Sprintf("AttachStdin: %t", c.AttachStdin),
|
||||
fmt.Sprintf("AttachStdout: %t", c.AttachStdout),
|
||||
fmt.Sprintf("AttachStderr: %t", c.AttachStderr),
|
||||
fmt.Sprintf("ExposedPorts: %v", sortMap(c.ExposedPorts)),
|
||||
fmt.Sprintf("Tty: %t", c.Tty),
|
||||
fmt.Sprintf("OpenStdin: %t", c.OpenStdin),
|
||||
fmt.Sprintf("StdinOnce: %t", c.StdinOnce),
|
||||
fmt.Sprintf("Env: %s", strings.Join(c.Env, ",")),
|
||||
fmt.Sprintf("Cmd: %s", strings.Join(c.Cmd, ",")),
|
||||
fmt.Sprintf("ArgsEscaped: %t", c.ArgsEscaped),
|
||||
fmt.Sprintf("Image: %s", c.Image),
|
||||
fmt.Sprintf("Volumes: %v", sortMap(c.Volumes)),
|
||||
fmt.Sprintf("Workdir: %s", c.Workdir),
|
||||
fmt.Sprintf("Entrypoint: %s", strings.Join(c.Entrypoint, ",")),
|
||||
fmt.Sprintf("NetworkDisabled: %t", c.NetworkDisabled),
|
||||
fmt.Sprintf("MacAddress: %s", c.MacAddress),
|
||||
fmt.Sprintf("OnBuild: %s", strings.Join(c.OnBuild, ",")),
|
||||
fmt.Sprintf("Labels: %v", c.Labels),
|
||||
fmt.Sprintf("StopSignal: %s", c.StopSignal),
|
||||
fmt.Sprintf("StopTimeout: %d", c.StopTimeout),
|
||||
fmt.Sprintf("Shell: %s", strings.Join(c.Shell, ",")),
|
||||
}
|
||||
}
|
||||
|
||||
type ConfigSchema struct {
|
||||
@@ -99,10 +149,17 @@ type ConfigSchema struct {
|
||||
}
|
||||
|
||||
func getImage(p Prepper) (Image, error) {
|
||||
// see if the image name has tag provided, if not add latest as tag
|
||||
if !IsTar(p.GetSource()) && !HasTag(p.GetSource()) {
|
||||
p.SetSource(p.GetSource() + LatestTag)
|
||||
}
|
||||
output.PrintToStdErr("Retrieving image %s from source %s\n", p.GetSource(), p.Name())
|
||||
imgPath, err := p.GetFileSystem()
|
||||
if err != nil {
|
||||
return Image{}, err
|
||||
// return image with FSPath so it can be cleaned up
|
||||
return Image{
|
||||
FSPath: imgPath,
|
||||
}, err
|
||||
}
|
||||
|
||||
config, err := p.GetConfig()
|
||||
@@ -128,6 +185,14 @@ func getImageFromTar(tarPath string) (string, error) {
|
||||
}
|
||||
|
||||
func GetFileSystemFromReference(ref types.ImageReference, imgSrc types.ImageSource, path string, whitelist []string) error {
|
||||
var err error
|
||||
if imgSrc == nil {
|
||||
imgSrc, err = ref.NewImageSource(nil)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer imgSrc.Close()
|
||||
img, err := ref.NewImage(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -208,7 +273,16 @@ func CleanupImage(image Image) {
|
||||
if image.FSPath != "" {
|
||||
logrus.Infof("Removing image filesystem directory %s from system", image.FSPath)
|
||||
if err := os.RemoveAll(image.FSPath); err != nil {
|
||||
logrus.Error(err.Error())
|
||||
logrus.Warn(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sortMap(m map[string]struct{}) string {
|
||||
pairs := make([]string, 0)
|
||||
for key := range m {
|
||||
pairs = append(pairs, fmt.Sprintf("%s:%s", key, m[key]))
|
||||
}
|
||||
sort.Strings(pairs)
|
||||
return strings.Join(pairs, " ")
|
||||
}
|
||||
|
||||
+10
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -21,11 +21,14 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const LatestTag string = ":latest"
|
||||
|
||||
func GetImageLayers(pathToImage string) []string {
|
||||
layers := []string{}
|
||||
contents, err := ioutil.ReadDir(pathToImage)
|
||||
@@ -67,3 +70,9 @@ func copyToFile(outfile string, r io.Reader) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checks to see if an image string contains a tag.
|
||||
func HasTag(image string) bool {
|
||||
tagRegex := regexp.MustCompile(".*:[^/]+$")
|
||||
return tagRegex.MatchString(image)
|
||||
}
|
||||
|
||||
+10
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -32,25 +32,29 @@ type TarPrepper struct {
|
||||
Client *client.Client
|
||||
}
|
||||
|
||||
func (p TarPrepper) Name() string {
|
||||
func (p *TarPrepper) Name() string {
|
||||
return "Tar Archive"
|
||||
}
|
||||
|
||||
func (p TarPrepper) GetSource() string {
|
||||
func (p *TarPrepper) GetSource() string {
|
||||
return p.Source
|
||||
}
|
||||
|
||||
func (p TarPrepper) GetImage() (Image, error) {
|
||||
func (p *TarPrepper) SetSource(source string) {
|
||||
p.Source = source
|
||||
}
|
||||
|
||||
func (p *TarPrepper) GetImage() (Image, error) {
|
||||
image, err := getImage(p)
|
||||
image.Type = ImageTypeTar
|
||||
return image, err
|
||||
}
|
||||
|
||||
func (p TarPrepper) GetFileSystem() (string, error) {
|
||||
func (p *TarPrepper) GetFileSystem() (string, error) {
|
||||
return getImageFromTar(p.Source)
|
||||
}
|
||||
|
||||
func (p TarPrepper) GetConfig() (ConfigSchema, error) {
|
||||
func (p *TarPrepper) GetConfig() (ConfigSchema, error) {
|
||||
tempDir, err := ioutil.TempDir("", ".container-diff")
|
||||
if err != nil {
|
||||
return ConfigSchema{}, nil
|
||||
|
||||
+62
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Google, Inc. All rights reserved.
|
||||
Copyright 2018 Google, Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -18,14 +18,26 @@ package util
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"github.com/sirupsen/logrus"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Map of target:linkname
|
||||
var hardlinks = make(map[string]string)
|
||||
|
||||
type OriginalPerm struct {
|
||||
path string
|
||||
perm os.FileMode
|
||||
}
|
||||
|
||||
func unpackTar(tr *tar.Reader, path string, whitelist []string) error {
|
||||
originalPerms := make([]OriginalPerm, 0)
|
||||
for {
|
||||
header, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
@@ -37,7 +49,7 @@ func unpackTar(tr *tar.Reader, path string, whitelist []string) error {
|
||||
return err
|
||||
}
|
||||
if strings.Contains(header.Name, ".wh.") {
|
||||
rmPath := filepath.Join(path, header.Name)
|
||||
rmPath := filepath.Clean(filepath.Join(path, header.Name))
|
||||
// Remove the .wh file if it was extracted.
|
||||
if _, err := os.Stat(rmPath); !os.IsNotExist(err) {
|
||||
if err := os.Remove(rmPath); err != nil {
|
||||
@@ -52,7 +64,7 @@ func unpackTar(tr *tar.Reader, path string, whitelist []string) error {
|
||||
}
|
||||
continue
|
||||
}
|
||||
target := filepath.Join(path, header.Name)
|
||||
target := filepath.Clean(filepath.Join(path, header.Name))
|
||||
// Make sure the target isn't part of the whitelist
|
||||
if checkWhitelist(target, whitelist) {
|
||||
continue
|
||||
@@ -63,6 +75,17 @@ func unpackTar(tr *tar.Reader, path string, whitelist []string) error {
|
||||
// if its a dir and it doesn't exist create it
|
||||
case tar.TypeDir:
|
||||
if _, err := os.Stat(target); os.IsNotExist(err) {
|
||||
if mode.Perm()&(1<<(uint(7))) == 0 {
|
||||
logrus.Debugf("Write permission bit not set on %s by default; setting manually", target)
|
||||
originalMode := mode
|
||||
mode = mode | (1 << uint(7))
|
||||
// keep track of original file permission to reset later
|
||||
originalPerms = append(originalPerms, OriginalPerm{
|
||||
path: target,
|
||||
perm: originalMode,
|
||||
})
|
||||
}
|
||||
logrus.Debugf("Creating directory %s with permissions %v", target, mode)
|
||||
if err := os.MkdirAll(target, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -91,6 +114,7 @@ func unpackTar(tr *tar.Reader, path string, whitelist []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
logrus.Debugf("Creating file %s with permissions %v", target, mode)
|
||||
currFile, err := os.Create(target)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error creating file %s %s", target, err)
|
||||
@@ -119,8 +143,42 @@ func unpackTar(tr *tar.Reader, path string, whitelist []string) error {
|
||||
if err = os.Symlink(header.Linkname, target); err != nil {
|
||||
logrus.Errorf("Failed to create symlink between %s and %s: %s", header.Linkname, target, err)
|
||||
}
|
||||
case tar.TypeLink:
|
||||
linkname := filepath.Clean(filepath.Join(path, header.Linkname))
|
||||
// Check if the linkname already exists
|
||||
if _, err := os.Stat(linkname); !os.IsNotExist(err) {
|
||||
// If it exists, create the hard link
|
||||
resolveHardlink(linkname, target)
|
||||
} else {
|
||||
hardlinks[target] = linkname
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for target, linkname := range hardlinks {
|
||||
logrus.Info("Resolving hard links.")
|
||||
if _, err := os.Stat(linkname); !os.IsNotExist(err) {
|
||||
// If it exists, create the hard link
|
||||
if err := resolveHardlink(linkname, target); err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("Unable to create hard link from %s to %s", linkname, target))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset all original file
|
||||
for _, perm := range originalPerms {
|
||||
if err := os.Chmod(perm.path, perm.perm); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveHardlink(linkname, target string) error {
|
||||
if err := os.Link(linkname, target); err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.Debugf("Created hard link from %s to %s", linkname, target)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user