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