github.com/chenchun/docker@v1.3.2-0.20150629222414-20467faf132b/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  				return nil, nil, false, errors.New("Illegal exclusion pattern: !")
    44  			}
    45  			exceptions = true
    46  		}
    47  		pattern = filepath.Clean(pattern)
    48  		cleanedPatterns = append(cleanedPatterns, pattern)
    49  		if Exclusion(pattern) {
    50  			pattern = pattern[1:]
    51  		}
    52  		patternDirs = append(patternDirs, strings.Split(pattern, "/"))
    53  	}
    54  
    55  	return cleanedPatterns, patternDirs, exceptions, nil
    56  }
    57  
    58  // Matches returns true if file matches any of the patterns
    59  // and isn't excluded by any of the subsequent patterns.
    60  func Matches(file string, patterns []string) (bool, error) {
    61  	file = filepath.Clean(file)
    62  
    63  	if file == "." {
    64  		// Don't let them exclude everything, kind of silly.
    65  		return false, nil
    66  	}
    67  
    68  	patterns, patDirs, _, err := CleanPatterns(patterns)
    69  	if err != nil {
    70  		return false, err
    71  	}
    72  
    73  	return OptimizedMatches(file, patterns, patDirs)
    74  }
    75  
    76  // OptimizedMatches is basically the same as fileutils.Matches() but optimized for archive.go.
    77  // It will assume that the inputs have been preprocessed and therefore the function
    78  // doen't need to do as much error checking and clean-up. This was done to avoid
    79  // repeating these steps on each file being checked during the archive process.
    80  // The more generic fileutils.Matches() can't make these assumptions.
    81  func OptimizedMatches(file string, patterns []string, patDirs [][]string) (bool, error) {
    82  	matched := false
    83  	parentPath := filepath.Dir(file)
    84  	parentPathDirs := strings.Split(parentPath, "/")
    85  
    86  	for i, pattern := range patterns {
    87  		negative := false
    88  
    89  		if Exclusion(pattern) {
    90  			negative = true
    91  			pattern = pattern[1:]
    92  		}
    93  
    94  		match, err := filepath.Match(pattern, file)
    95  		if err != nil {
    96  			return false, err
    97  		}
    98  
    99  		if !match && parentPath != "." {
   100  			// Check to see if the pattern matches one of our parent dirs.
   101  			if len(patDirs[i]) <= len(parentPathDirs) {
   102  				match, _ = filepath.Match(strings.Join(patDirs[i], "/"),
   103  					strings.Join(parentPathDirs[:len(patDirs[i])], "/"))
   104  			}
   105  		}
   106  
   107  		if match {
   108  			matched = !negative
   109  		}
   110  	}
   111  
   112  	if matched {
   113  		logrus.Debugf("Skipping excluded path: %s", file)
   114  	}
   115  
   116  	return matched, nil
   117  }
   118  
   119  func CopyFile(src, dst string) (int64, error) {
   120  	cleanSrc := filepath.Clean(src)
   121  	cleanDst := filepath.Clean(dst)
   122  	if cleanSrc == cleanDst {
   123  		return 0, nil
   124  	}
   125  	sf, err := os.Open(cleanSrc)
   126  	if err != nil {
   127  		return 0, err
   128  	}
   129  	defer sf.Close()
   130  	if err := os.Remove(cleanDst); err != nil && !os.IsNotExist(err) {
   131  		return 0, err
   132  	}
   133  	df, err := os.Create(cleanDst)
   134  	if err != nil {
   135  		return 0, err
   136  	}
   137  	defer df.Close()
   138  	return io.Copy(df, sf)
   139  }
   140  
   141  func GetTotalUsedFds() int {
   142  	if fds, err := ioutil.ReadDir(fmt.Sprintf("/proc/%d/fd", os.Getpid())); err != nil {
   143  		logrus.Errorf("Error opening /proc/%d/fd: %s", os.Getpid(), err)
   144  	} else {
   145  		return len(fds)
   146  	}
   147  	return -1
   148  }
   149  
   150  // ReadSymlinkedDirectory returns the target directory of a symlink.
   151  // The target of the symbolic link may not be a file.
   152  func ReadSymlinkedDirectory(path string) (string, error) {
   153  	var realPath string
   154  	var err error
   155  	if realPath, err = filepath.Abs(path); err != nil {
   156  		return "", fmt.Errorf("unable to get absolute path for %s: %s", path, err)
   157  	}
   158  	if realPath, err = filepath.EvalSymlinks(realPath); err != nil {
   159  		return "", fmt.Errorf("failed to canonicalise path for %s: %s", path, err)
   160  	}
   161  	realPathInfo, err := os.Stat(realPath)
   162  	if err != nil {
   163  		return "", fmt.Errorf("failed to stat target '%s' of '%s': %s", realPath, path, err)
   164  	}
   165  	if !realPathInfo.Mode().IsDir() {
   166  		return "", fmt.Errorf("canonical path points to a file '%s'", realPath)
   167  	}
   168  	return realPath, nil
   169  }