github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/cf/appfiles/app_files.go (about)

     1  package appfiles
     2  
     3  import (
     4  	"crypto/sha1"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  
    12  	"code.cloudfoundry.org/cli/cf/models"
    13  	"code.cloudfoundry.org/gofileutils/fileutils"
    14  	securejoin "github.com/cyphar/filepath-securejoin"
    15  )
    16  
    17  const windowsPathPrefix = `\\?\`
    18  
    19  //go:generate counterfeiter . AppFiles
    20  
    21  type AppFiles interface {
    22  	AppFilesInDir(dir string) (appFiles []models.AppFileFields, err error)
    23  	CopyFiles(appFiles []models.AppFileFields, fromDir, toDir string) (err error)
    24  	CountFiles(directory string) int64
    25  	WalkAppFiles(dir string, onEachFile func(string, string) error) (err error)
    26  }
    27  
    28  type ApplicationFiles struct{}
    29  
    30  func (appfiles ApplicationFiles) AppFilesInDir(dir string) ([]models.AppFileFields, error) {
    31  	appFiles := []models.AppFileFields{}
    32  
    33  	fullDirPath, toplevelErr := filepath.Abs(dir)
    34  	if toplevelErr != nil {
    35  		return appFiles, toplevelErr
    36  	}
    37  
    38  	toplevelErr = appfiles.WalkAppFiles(fullDirPath, func(fileName string, fullPath string) error {
    39  		fileInfo, err := os.Lstat(fullPath)
    40  		if err != nil {
    41  			return err
    42  		}
    43  
    44  		appFile := models.AppFileFields{
    45  			Path: filepath.ToSlash(fileName),
    46  			Size: fileInfo.Size(),
    47  		}
    48  
    49  		if fileInfo.IsDir() {
    50  			appFile.Sha1 = "0"
    51  			appFile.Size = 0
    52  		} else {
    53  			sha, err := appfiles.shaFile(fullPath)
    54  			if err != nil {
    55  				return err
    56  			}
    57  			appFile.Sha1 = sha
    58  		}
    59  
    60  		appFiles = append(appFiles, appFile)
    61  
    62  		return nil
    63  	})
    64  
    65  	return appFiles, toplevelErr
    66  }
    67  
    68  func (appfiles ApplicationFiles) shaFile(fullPath string) (string, error) {
    69  	hash := sha1.New()
    70  	file, err := os.Open(fullPath)
    71  	if err != nil {
    72  		return "", err
    73  	}
    74  	defer file.Close()
    75  
    76  	_, err = io.Copy(hash, file)
    77  	if err != nil {
    78  		return "", err
    79  	}
    80  
    81  	return fmt.Sprintf("%x", hash.Sum(nil)), nil
    82  }
    83  
    84  func (appfiles ApplicationFiles) CopyFiles(appFiles []models.AppFileFields, fromDir, toDir string) error {
    85  	for _, file := range appFiles {
    86  		err := func() error {
    87  			path, err := securejoin.SecureJoin(fromDir, file.Path)
    88  			if err != nil {
    89  				return err
    90  			}
    91  
    92  			fromPath, err := filepath.Abs(path)
    93  			if err != nil {
    94  				return err
    95  			}
    96  
    97  			if runtime.GOOS == "windows" {
    98  				fromPath = windowsPathPrefix + fromPath
    99  			}
   100  
   101  			srcFileInfo, err := os.Stat(fromPath)
   102  			if err != nil {
   103  				return err
   104  			}
   105  
   106  			path, err = securejoin.SecureJoin(toDir, file.Path)
   107  			if err != nil {
   108  				return err
   109  			}
   110  			toPath, err := filepath.Abs(path)
   111  			if err != nil {
   112  				return err
   113  			}
   114  
   115  			if runtime.GOOS == "windows" {
   116  				toPath = windowsPathPrefix + toPath
   117  			}
   118  
   119  			if srcFileInfo.IsDir() {
   120  				err = os.MkdirAll(toPath, srcFileInfo.Mode())
   121  				if err != nil {
   122  					return err
   123  				}
   124  				return nil
   125  			}
   126  
   127  			return appfiles.copyFile(fromPath, toPath, srcFileInfo.Mode())
   128  		}()
   129  
   130  		if err != nil {
   131  			return err
   132  		}
   133  	}
   134  
   135  	return nil
   136  }
   137  
   138  func (appfiles ApplicationFiles) copyFile(srcPath string, dstPath string, fileMode os.FileMode) error {
   139  	dst, err := fileutils.Create(dstPath)
   140  	if err != nil {
   141  		return err
   142  	}
   143  	defer dst.Close()
   144  
   145  	if runtime.GOOS != "windows" {
   146  		err = dst.Chmod(fileMode)
   147  		if err != nil {
   148  			return err
   149  		}
   150  	}
   151  
   152  	src, err := os.Open(srcPath)
   153  	if err != nil {
   154  		return err
   155  	}
   156  	defer src.Close()
   157  
   158  	_, err = io.Copy(dst, src)
   159  	if err != nil {
   160  		return err
   161  	}
   162  
   163  	return nil
   164  }
   165  
   166  func (appfiles ApplicationFiles) CountFiles(directory string) int64 {
   167  	var count int64
   168  	appfiles.WalkAppFiles(directory, func(_, _ string) error {
   169  		count++
   170  		return nil
   171  	})
   172  	return count
   173  }
   174  
   175  func (appfiles ApplicationFiles) WalkAppFiles(dir string, onEachFile func(string, string) error) error {
   176  	cfIgnore := loadIgnoreFile(dir)
   177  	walkFunc := func(fullPath string, f os.FileInfo, err error) error {
   178  		fileRelativePath, _ := filepath.Rel(dir, fullPath)
   179  		fileRelativeUnixPath := filepath.ToSlash(fileRelativePath)
   180  
   181  		if err != nil && runtime.GOOS == "windows" {
   182  			f, err = os.Lstat(windowsPathPrefix + fullPath)
   183  			if err != nil {
   184  				return err
   185  			}
   186  			fullPath = windowsPathPrefix + fullPath
   187  		}
   188  
   189  		if fullPath == dir {
   190  			return nil
   191  		}
   192  
   193  		if cfIgnore.FileShouldBeIgnored(fileRelativeUnixPath) {
   194  			if err == nil && f.IsDir() {
   195  				return filepath.SkipDir
   196  			}
   197  			return nil
   198  		}
   199  
   200  		if err != nil {
   201  			return err
   202  		}
   203  
   204  		if !f.Mode().IsRegular() && !f.IsDir() {
   205  			return nil
   206  		}
   207  
   208  		return onEachFile(fileRelativePath, fullPath)
   209  	}
   210  
   211  	return filepath.Walk(dir, walkFunc)
   212  }
   213  
   214  func loadIgnoreFile(dir string) CfIgnore {
   215  	fileContents, err := ioutil.ReadFile(filepath.Join(dir, ".cfignore"))
   216  	if err != nil {
   217  		return NewCfIgnore("")
   218  	}
   219  
   220  	return NewCfIgnore(string(fileContents))
   221  }