148 lines
3.9 KiB
Go
148 lines
3.9 KiB
Go
|
// Licensed to Elasticsearch B.V. under one or more contributor
|
||
|
// license agreements. See the NOTICE file distributed with
|
||
|
// this work for additional information regarding copyright
|
||
|
// ownership. Elasticsearch B.V. licenses this file to you under
|
||
|
// the Apache License, Version 2.0 (the "License"); you may
|
||
|
// not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing,
|
||
|
// software distributed under the License is distributed on an
|
||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||
|
// KIND, either express or implied. See the License for the
|
||
|
// specific language governing permissions and limitations
|
||
|
// under the License.
|
||
|
|
||
|
package mage
|
||
|
|
||
|
import (
|
||
|
"io"
|
||
|
"io/ioutil"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"regexp"
|
||
|
|
||
|
"github.com/pkg/errors"
|
||
|
)
|
||
|
|
||
|
// Copy copies a file or a directory (recursively) and preserves the permissions.
|
||
|
func Copy(src, dest string) error {
|
||
|
copy := &CopyTask{Source: src, Dest: dest}
|
||
|
return copy.Execute()
|
||
|
}
|
||
|
|
||
|
// CopyTask copies a file or directory (recursively) and preserves the permissions.
|
||
|
type CopyTask struct {
|
||
|
Source string // Source directory or file.
|
||
|
Dest string // Destination directory or file.
|
||
|
Mode os.FileMode // Mode to use for copied files. Defaults to preserve permissions.
|
||
|
DirMode os.FileMode // Mode to use for copied dirs. Defaults to preserve permissions.
|
||
|
Exclude []string // Exclude paths that match these regular expressions.
|
||
|
excludes []*regexp.Regexp // Compiled exclude regexes.
|
||
|
}
|
||
|
|
||
|
// Execute executes the copy and returns an error of there is a failure.
|
||
|
func (t *CopyTask) Execute() error {
|
||
|
if err := t.init(); err != nil {
|
||
|
return errors.Wrap(err, "copy failed")
|
||
|
}
|
||
|
|
||
|
info, err := os.Stat(t.Source)
|
||
|
if err != nil {
|
||
|
return errors.Wrapf(err, "copy failed: cannot stat source file %v", t.Source)
|
||
|
}
|
||
|
|
||
|
return errors.Wrap(t.recursiveCopy(t.Source, t.Dest, info), "copy failed")
|
||
|
}
|
||
|
|
||
|
func (t *CopyTask) init() error {
|
||
|
for _, excl := range t.Exclude {
|
||
|
re, err := regexp.Compile(excl)
|
||
|
if err != nil {
|
||
|
return errors.Wrapf(err, "bad exclude pattern %v", excl)
|
||
|
}
|
||
|
t.excludes = append(t.excludes, re)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (t *CopyTask) isExcluded(src string) bool {
|
||
|
for _, excl := range t.excludes {
|
||
|
if match := excl.MatchString(filepath.ToSlash(src)); match {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func (t *CopyTask) recursiveCopy(src, dest string, info os.FileInfo) error {
|
||
|
if info.IsDir() {
|
||
|
return t.dirCopy(src, dest, info)
|
||
|
}
|
||
|
return t.fileCopy(src, dest, info)
|
||
|
}
|
||
|
|
||
|
func (t *CopyTask) fileCopy(src, dest string, info os.FileInfo) error {
|
||
|
if t.isExcluded(src) {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
srcFile, err := os.Open(src)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer srcFile.Close()
|
||
|
|
||
|
if !info.Mode().IsRegular() {
|
||
|
return errors.Errorf("failed to copy source file because it is not a " +
|
||
|
"regular file")
|
||
|
}
|
||
|
|
||
|
mode := t.Mode
|
||
|
if mode == 0 {
|
||
|
mode = info.Mode()
|
||
|
}
|
||
|
destFile, err := os.OpenFile(createDir(dest),
|
||
|
os.O_CREATE|os.O_TRUNC|os.O_WRONLY, mode&os.ModePerm)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer destFile.Close()
|
||
|
|
||
|
if _, err = io.Copy(destFile, srcFile); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return destFile.Close()
|
||
|
}
|
||
|
|
||
|
func (t *CopyTask) dirCopy(src, dest string, info os.FileInfo) error {
|
||
|
if t.isExcluded(src) {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
mode := t.DirMode
|
||
|
if mode == 0 {
|
||
|
mode = info.Mode()
|
||
|
}
|
||
|
if err := os.MkdirAll(dest, mode&os.ModePerm); err != nil {
|
||
|
return errors.Wrap(err, "failed creating dirs")
|
||
|
}
|
||
|
|
||
|
contents, err := ioutil.ReadDir(src)
|
||
|
if err != nil {
|
||
|
return errors.Wrapf(err, "failed to read dir %v", src)
|
||
|
}
|
||
|
|
||
|
for _, info := range contents {
|
||
|
srcFile := filepath.Join(src, info.Name())
|
||
|
destFile := filepath.Join(dest, info.Name())
|
||
|
if err = t.recursiveCopy(srcFile, destFile, info); err != nil {
|
||
|
return errors.Wrapf(err, "failed to copy %v to %v", srcFile, destFile)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|