github.com/michael-k/docker@v1.7.0-rc2/pkg/fileutils/fileutils.go (about)

     1  package fileutils
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/Sirupsen/logrus"
    13  )
    14  
    15  func Exclusion(pattern string) bool {
    16  	return pattern[0] == '!'
    17  }
    18  
    19  func Empty(pattern string) bool {
    20  	return pattern == ""
    21  }
    22  
    23  // Cleanpatterns takes a slice of patterns returns a new
    24  // slice of patterns cleaned with filepath.Clean, stripped
    25  // of any empty patterns and lets the caller know whether the
    26  // slice contains any exception patterns (prefixed with !).
    27  func CleanPatterns(patterns []string) ([]string, [][]string, bool, error) {
    28  	// Loop over exclusion patterns and:
    29  	// 1. Clean them up.
    30  	// 2. Indicate whether we are dealing with any exception rules.
    31  	// 3. Error if we see a single exclusion marker on it's own (!).
    32  	cleanedPatterns := []string{}
    33  	patternDirs := [][]string{}
    34  	exceptions := false
    35  	for _, pattern := range patterns {
    36  		// Eliminate leading and trailing whitespace.
    37  		pattern = strings.TrimSpace(pattern)
    38  		if Empty(pattern) {
    39  			continue
    40  		}
    41  		if Exclusion(pattern) {
    42  			if len(pattern) == 1 {
    43  				logrus.Errorf("Illegal exclusion pattern: %s", pattern)
    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  // Matches 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  			logrus.Errorf("Error matching: %s (pattern: %s)", file, pattern)
    98  			return false, err
    99  		}
   100  
   101  		if !match && parentPath != "." {
   102  			// Check to see if the pattern matches one of our parent dirs.
   103  			if len(patDirs[i]) <= len(parentPathDirs) {
   104  				match, _ = filepath.Match(strings.Join(patDirs[i], "/"),
   105  					strings.Join(parentPathDirs[:len(patDirs[i])], "/"))
   106  			}
   107  		}
   108  
   109  		if match {
   110  			matched = !negative
   111  		}
   112  	}
   113  
   114  	if matched {
   115  		logrus.Debugf("Skipping excluded path: %s", file)
   116  	}
   117  	return matched, nil
   118  }
   119  
   120  func CopyFile(src, dst string) (int64, error) {
   121  	cleanSrc := filepath.Clean(src)
   122  	cleanDst := filepath.Clean(dst)
   123  	if cleanSrc == cleanDst {
   124  		return 0, nil
   125  	}
   126  	sf, err := os.Open(cleanSrc)
   127  	if err != nil {
   128  		return 0, err
   129  	}
   130  	defer sf.Close()
   131  	if err := os.Remove(cleanDst); err != nil && !os.IsNotExist(err) {
   132  		return 0, err
   133  	}
   134  	df, err := os.Create(cleanDst)
   135  	if err != nil {
   136  		return 0, err
   137  	}
   138  	defer df.Close()
   139  	return io.Copy(df, sf)
   140  }
   141  
   142  func GetTotalUsedFds() int {
   143  	if fds, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil {
   144  		logrus.Errorf("Error opening /proc/%d/fd: %s", os.Getpid(), err)
   145  	} else {
   146  		return len(fds)
   147  	}
   148  	return -1
   149  }
   150  
   151  // ReadSymlinkedDirectory returns the target directory of a symlink.
   152  // The target of the symbolic link may not be a file.
   153  func ReadSymlinkedDirectory(path string) (string, error) {
   154  	var realPath string
   155  	var err error
   156  	if realPath, err = filepath.Abs(path); err != nil {
   157  		return "", fmt.Errorf("unable to get absolute path for %s: %s", path, err)
   158  	}
   159  	if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
   160  		return "", fmt.Errorf("failed to canonicalise path for %s: %s", path, err)
   161  	}
   162  	realPathInfo, err := os.Stat(realPath)
   163  	if err != nil {
   164  		return "", fmt.Errorf("failed to stat target '%s' of '%s': %s", realPath, path, err)
   165  	}
   166  	if !realPathInfo.Mode().IsDir() {
   167  		return "", fmt.Errorf("canonical path points to a file '%s'", realPath)
   168  	}
   169  	return realPath, nil
   170  }