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 }