github.com/tompao/docker@v1.9.1/pkg/fileutils/fileutils.go (about)

     1  package fileutils
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	"github.com/Sirupsen/logrus"
    12  )
    13  
    14  // exclusion return true if the specified pattern is an exclusion
    15  func exclusion(pattern string) bool {
    16  	return pattern[0] == '!'
    17  }
    18  
    19  // empty return true if the specified pattern is empty
    20  func empty(pattern string) bool {
    21  	return pattern == ""
    22  }
    23  
    24  // CleanPatterns takes a slice of patterns returns a new
    25  // slice of patterns cleaned with filepath.Clean, stripped
    26  // of any empty patterns and lets the caller know whether the
    27  // slice contains any exception patterns (prefixed with !).
    28  func CleanPatterns(patterns []string) ([]string, [][]string, bool, error) {
    29  	// Loop over exclusion patterns and:
    30  	// 1. Clean them up.
    31  	// 2. Indicate whether we are dealing with any exception rules.
    32  	// 3. Error if we see a single exclusion marker on it's own (!).
    33  	cleanedPatterns := []string{}
    34  	patternDirs := [][]string{}
    35  	exceptions := false
    36  	for _, pattern := range patterns {
    37  		// Eliminate leading and trailing whitespace.
    38  		pattern = strings.TrimSpace(pattern)
    39  		if empty(pattern) {
    40  			continue
    41  		}
    42  		if exclusion(pattern) {
    43  			if len(pattern) == 1 {
    44  				return nil, nil, false, errors.New("Illegal exclusion pattern: !")
    45  			}
    46  			exceptions = true
    47  		}
    48  		pattern = filepath.Clean(pattern)
    49  		cleanedPatterns = append(cleanedPatterns, pattern)
    50  		if exclusion(pattern) {
    51  			pattern = pattern[1:]
    52  		}
    53  		patternDirs = append(patternDirs, strings.Split(pattern, "/"))
    54  	}
    55  
    56  	return cleanedPatterns, patternDirs, exceptions, nil
    57  }
    58  
    59  // Matches returns true if file matches any of the patterns
    60  // and isn't excluded by any of the subsequent patterns.
    61  func Matches(file string, patterns []string) (bool, error) {
    62  	file = filepath.Clean(file)
    63  
    64  	if file == "." {
    65  		// Don't let them exclude everything, kind of silly.
    66  		return false, nil
    67  	}
    68  
    69  	patterns, patDirs, _, err := CleanPatterns(patterns)
    70  	if err != nil {
    71  		return false, err
    72  	}
    73  
    74  	return OptimizedMatches(file, patterns, patDirs)
    75  }
    76  
    77  // OptimizedMatches is basically the same as fileutils.Matches() but optimized for archive.go.
    78  // It will assume that the inputs have been preprocessed and therefore the function
    79  // doen't need to do as much error checking and clean-up. This was done to avoid
    80  // repeating these steps on each file being checked during the archive process.
    81  // The more generic fileutils.Matches() can't make these assumptions.
    82  func OptimizedMatches(file string, patterns []string, patDirs [][]string) (bool, error) {
    83  	matched := false
    84  	parentPath := filepath.Dir(file)
    85  	parentPathDirs := strings.Split(parentPath, "/")
    86  
    87  	for i, pattern := range patterns {
    88  		negative := false
    89  
    90  		if exclusion(pattern) {
    91  			negative = true
    92  			pattern = pattern[1:]
    93  		}
    94  
    95  		match, err := filepath.Match(pattern, file)
    96  		if err != nil {
    97  			return false, err
    98  		}
    99  
   100  		if !match && parentPath != "." {
   101  			// Check to see if the pattern matches one of our parent dirs.
   102  			if len(patDirs[i]) <= len(parentPathDirs) {
   103  				match, _ = filepath.Match(strings.Join(patDirs[i], "/"),
   104  					strings.Join(parentPathDirs[:len(patDirs[i])], "/"))
   105  			}
   106  		}
   107  
   108  		if match {
   109  			matched = !negative
   110  		}
   111  	}
   112  
   113  	if matched {
   114  		logrus.Debugf("Skipping excluded path: %s", file)
   115  	}
   116  
   117  	return matched, nil
   118  }
   119  
   120  // CopyFile copies from src to dst until either EOF is reached
   121  // on src or an error occurs. It verifies src exists and remove
   122  // the dst if it exists.
   123  func CopyFile(src, dst string) (int64, error) {
   124  	cleanSrc := filepath.Clean(src)
   125  	cleanDst := filepath.Clean(dst)
   126  	if cleanSrc == cleanDst {
   127  		return 0, nil
   128  	}
   129  	sf, err := os.Open(cleanSrc)
   130  	if err != nil {
   131  		return 0, err
   132  	}
   133  	defer sf.Close()
   134  	if err := os.Remove(cleanDst); err != nil && !os.IsNotExist(err) {
   135  		return 0, err
   136  	}
   137  	df, err := os.Create(cleanDst)
   138  	if err != nil {
   139  		return 0, err
   140  	}
   141  	defer df.Close()
   142  	return io.Copy(df, sf)
   143  }
   144  
   145  // ReadSymlinkedDirectory returns the target directory of a symlink.
   146  // The target of the symbolic link may not be a file.
   147  func ReadSymlinkedDirectory(path string) (string, error) {
   148  	var realPath string
   149  	var err error
   150  	if realPath, err = filepath.Abs(path); err != nil {
   151  		return "", fmt.Errorf("unable to get absolute path for %s: %s", path, err)
   152  	}
   153  	if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
   154  		return "", fmt.Errorf("failed to canonicalise path for %s: %s", path, err)
   155  	}
   156  	realPathInfo, err := os.Stat(realPath)
   157  	if err != nil {
   158  		return "", fmt.Errorf("failed to stat target '%s' of '%s': %s", realPath, path, err)
   159  	}
   160  	if !realPathInfo.Mode().IsDir() {
   161  		return "", fmt.Errorf("canonical path points to a file '%s'", realPath)
   162  	}
   163  	return realPath, nil
   164  }
   165  
   166  // CreateIfNotExists creates a file or a directory only if it does not already exist.
   167  func CreateIfNotExists(path string, isDir bool) error {
   168  	if _, err := os.Stat(path); err != nil {
   169  		if os.IsNotExist(err) {
   170  			if isDir {
   171  				return os.MkdirAll(path, 0755)
   172  			}
   173  			if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
   174  				return err
   175  			}
   176  			f, err := os.OpenFile(path, os.O_CREATE, 0755)
   177  			if err != nil {
   178  				return err
   179  			}
   180  			f.Close()
   181  		}
   182  	}
   183  	return nil
   184  }