github.com/cobalt77/jfrog-client-go@v0.14.5/utils/io/fileutils/files.go (about) 1 package fileutils 2 3 import ( 4 "bufio" 5 "bytes" 6 "errors" 7 "io" 8 "io/ioutil" 9 "net/url" 10 "os" 11 "os/user" 12 "path/filepath" 13 "strings" 14 15 "github.com/cobalt77/jfrog-client-go/utils/errorutils" 16 "github.com/cobalt77/jfrog-client-go/utils/io/fileutils/checksum" 17 ) 18 19 const ( 20 SYMLINK_FILE_CONTENT = "" 21 File ItemType = "file" 22 Dir ItemType = "dir" 23 ) 24 25 func GetFileSeparator() string { 26 return string(os.PathSeparator) 27 } 28 29 // Check if path exists. 30 // If path points at a symlink and `preserveSymLink == true`, 31 // function will return `true` regardless of the symlink target 32 func IsPathExists(path string, preserveSymLink bool) bool { 33 _, err := GetFileInfo(path, preserveSymLink) 34 return !os.IsNotExist(err) 35 } 36 37 // Check if path points at a file. 38 // If path points at a symlink and `preserveSymLink == true`, 39 // function will return `true` regardless of the symlink target 40 func IsFileExists(path string, preserveSymLink bool) (bool, error) { 41 fileInfo, err := GetFileInfo(path, preserveSymLink) 42 if err != nil { 43 if os.IsNotExist(err) { // If doesn't exist, don't omit an error 44 return false, nil 45 } 46 return false, errorutils.CheckError(err) 47 } 48 return !fileInfo.IsDir(), nil 49 } 50 51 // Check if path points at a directory. 52 // If path points at a symlink and `preserveSymLink == true`, 53 // function will return `false` regardless of the symlink target 54 func IsDirExists(path string, preserveSymLink bool) (bool, error) { 55 fileInfo, err := GetFileInfo(path, preserveSymLink) 56 if err != nil { 57 if os.IsNotExist(err) { // If doesn't exist, don't omit an error 58 return false, nil 59 } 60 return false, errorutils.CheckError(err) 61 } 62 return fileInfo.IsDir(), nil 63 } 64 65 // Get the file info of the file in path. 66 // If path points at a symlink and `preserveSymLink == true`, return the file info of the symlink instead 67 func GetFileInfo(path string, preserveSymLink bool) (fileInfo os.FileInfo, err error) { 68 if preserveSymLink { 69 fileInfo, err = os.Lstat(path) 70 } else { 71 fileInfo, err = os.Stat(path) 72 } 73 // We should not do CheckError here, because the error is checked by the calling functions. 74 return fileInfo, err 75 } 76 77 func IsPathSymlink(path string) bool { 78 f, _ := os.Lstat(path) 79 return f != nil && IsFileSymlink(f) 80 } 81 82 func IsFileSymlink(file os.FileInfo) bool { 83 return file.Mode()&os.ModeSymlink != 0 84 } 85 86 func GetFileAndDirFromPath(path string) (fileName, dir string) { 87 index1 := strings.LastIndex(path, "/") 88 index2 := strings.LastIndex(path, "\\") 89 var index int 90 if index1 >= index2 { 91 index = index1 92 } else { 93 index = index2 94 } 95 if index != -1 { 96 fileName = path[index+1:] 97 dir = path[:index] 98 return 99 } 100 fileName = path 101 dir = "" 102 return 103 } 104 105 // Get the local path and filename from original file name and path according to targetPath 106 func GetLocalPathAndFile(originalFileName, relativePath, targetPath string, flat bool) (localTargetPath, fileName string) { 107 targetFileName, targetDirPath := GetFileAndDirFromPath(targetPath) 108 localTargetPath = targetDirPath 109 if !flat { 110 localTargetPath = filepath.Join(targetDirPath, relativePath) 111 } 112 113 fileName = originalFileName 114 if targetFileName != "" { 115 fileName = targetFileName 116 } 117 return 118 } 119 120 // Return the recursive list of files and directories in the specified path 121 func ListFilesRecursiveWalkIntoDirSymlink(path string, walkIntoDirSymlink bool) (fileList []string, err error) { 122 fileList = []string{} 123 err = Walk(path, func(path string, f os.FileInfo, err error) error { 124 fileList = append(fileList, path) 125 return nil 126 }, walkIntoDirSymlink) 127 err = errorutils.CheckError(err) 128 return 129 } 130 131 // Return all files with the specified extension in the specified path. Not recursive. 132 func ListFilesWithExtension(path, ext string) ([]string, error) { 133 sep := GetFileSeparator() 134 if !strings.HasSuffix(path, sep) { 135 path += sep 136 } 137 fileList := []string{} 138 files, _ := ioutil.ReadDir(path) 139 path = strings.TrimPrefix(path, "."+sep) 140 141 for _, f := range files { 142 if !strings.HasSuffix(f.Name(), ext) { 143 continue 144 } 145 filePath := path + f.Name() 146 exists, err := IsFileExists(filePath, false) 147 if err != nil { 148 return nil, err 149 } 150 if exists { 151 fileList = append(fileList, filePath) 152 continue 153 } 154 155 // Checks if the filepath is a symlink. 156 if IsPathSymlink(filePath) { 157 // Gets the file info of the symlink. 158 file, err := GetFileInfo(filePath, false) 159 if errorutils.CheckError(err) != nil { 160 return nil, err 161 } 162 // Checks if the symlink is a file. 163 if !file.IsDir() { 164 fileList = append(fileList, filePath) 165 } 166 } 167 } 168 return fileList, nil 169 } 170 171 // Return the list of files and directories in the specified path 172 func ListFiles(path string, includeDirs bool) ([]string, error) { 173 sep := GetFileSeparator() 174 if !strings.HasSuffix(path, sep) { 175 path += sep 176 } 177 fileList := []string{} 178 files, _ := ioutil.ReadDir(path) 179 path = strings.TrimPrefix(path, "."+sep) 180 181 for _, f := range files { 182 filePath := path + f.Name() 183 exists, err := IsFileExists(filePath, false) 184 if err != nil { 185 return nil, err 186 } 187 if exists || IsPathSymlink(filePath) { 188 fileList = append(fileList, filePath) 189 } else if includeDirs { 190 isDir, err := IsDirExists(filePath, false) 191 if err != nil { 192 return nil, err 193 } 194 if isDir { 195 fileList = append(fileList, filePath) 196 } 197 } 198 } 199 return fileList, nil 200 } 201 202 func GetUploadRequestContent(file *os.File) io.Reader { 203 if file == nil { 204 return bytes.NewBuffer([]byte(SYMLINK_FILE_CONTENT)) 205 } 206 return bufio.NewReader(file) 207 } 208 209 func GetFileSize(file *os.File) (int64, error) { 210 size := int64(0) 211 if file != nil { 212 fileInfo, err := file.Stat() 213 if errorutils.CheckError(err) != nil { 214 return size, err 215 } 216 size = fileInfo.Size() 217 } 218 return size, nil 219 } 220 221 func CreateFilePath(localPath, fileName string) (string, error) { 222 if localPath != "" { 223 err := os.MkdirAll(localPath, 0777) 224 if errorutils.CheckError(err) != nil { 225 return "", err 226 } 227 fileName = filepath.Join(localPath, fileName) 228 } 229 return fileName, nil 230 } 231 232 func CreateDirIfNotExist(path string) error { 233 exist, err := IsDirExists(path, false) 234 if exist || err != nil { 235 return err 236 } 237 _, err = CreateFilePath(path, "") 238 return err 239 } 240 241 // Reads the content of the file in the source path and appends it to 242 // the file in the destination path. 243 func AppendFile(srcPath string, destFile *os.File) error { 244 srcFile, err := os.Open(srcPath) 245 err = errorutils.CheckError(err) 246 if err != nil { 247 return err 248 } 249 250 defer func() error { 251 err := srcFile.Close() 252 return errorutils.CheckError(err) 253 }() 254 255 reader := bufio.NewReader(srcFile) 256 257 writer := bufio.NewWriter(destFile) 258 buf := make([]byte, 1024000) 259 for { 260 n, err := reader.Read(buf) 261 if err != io.EOF { 262 err = errorutils.CheckError(err) 263 if err != nil { 264 return err 265 } 266 } 267 if n == 0 { 268 break 269 } 270 _, err = writer.Write(buf[:n]) 271 err = errorutils.CheckError(err) 272 if err != nil { 273 return err 274 } 275 } 276 err = writer.Flush() 277 return errorutils.CheckError(err) 278 } 279 280 func GetHomeDir() string { 281 home := os.Getenv("HOME") 282 if home != "" { 283 return home 284 } 285 home = os.Getenv("USERPROFILE") 286 if home != "" { 287 return home 288 } 289 user, err := user.Current() 290 if err == nil { 291 return user.HomeDir 292 } 293 return "" 294 } 295 296 func IsSshUrl(urlPath string) bool { 297 u, err := url.Parse(urlPath) 298 if err != nil { 299 return false 300 } 301 return strings.ToLower(u.Scheme) == "ssh" 302 } 303 304 func ReadFile(filePath string) ([]byte, error) { 305 content, err := ioutil.ReadFile(filePath) 306 err = errorutils.CheckError(err) 307 return content, err 308 } 309 310 func GetFileDetails(filePath string) (*FileDetails, error) { 311 var err error 312 details := new(FileDetails) 313 details.Checksum, err = calcChecksumDetails(filePath) 314 315 file, err := os.Open(filePath) 316 defer file.Close() 317 if errorutils.CheckError(err) != nil { 318 return nil, err 319 } 320 fileInfo, err := file.Stat() 321 if errorutils.CheckError(err) != nil { 322 return nil, err 323 } 324 details.Size = fileInfo.Size() 325 return details, nil 326 } 327 328 func calcChecksumDetails(filePath string) (ChecksumDetails, error) { 329 file, err := os.Open(filePath) 330 defer file.Close() 331 if errorutils.CheckError(err) != nil { 332 return ChecksumDetails{}, err 333 } 334 checksumInfo, err := checksum.Calc(file) 335 if err != nil { 336 return ChecksumDetails{}, err 337 } 338 return ChecksumDetails{Md5: checksumInfo[checksum.MD5], Sha1: checksumInfo[checksum.SHA1], Sha256: checksumInfo[checksum.SHA256]}, nil 339 } 340 341 type FileDetails struct { 342 Checksum ChecksumDetails 343 Size int64 344 } 345 346 type ChecksumDetails struct { 347 Md5 string 348 Sha1 string 349 Sha256 string 350 } 351 352 func CopyFile(dst, src string) error { 353 srcFile, err := os.Open(src) 354 if err != nil { 355 return err 356 } 357 defer srcFile.Close() 358 fileName, _ := GetFileAndDirFromPath(src) 359 dstPath, err := CreateFilePath(dst, fileName) 360 if err != nil { 361 return err 362 } 363 dstFile, err := os.Create(dstPath) 364 if err != nil { 365 return err 366 } 367 defer dstFile.Close() 368 io.Copy(dstFile, srcFile) 369 return nil 370 } 371 372 // Copy directory content from one path to another. 373 // includeDirs means to copy also the dirs if presented in the src folder. 374 // excludeNames - Skip files/dirs in the src folder that match names in provided slice. ONLY excludes first layer (only in src folder). 375 func CopyDir(fromPath, toPath string, includeDirs bool, excludeNames []string) error { 376 err := CreateDirIfNotExist(toPath) 377 if err != nil { 378 return err 379 } 380 381 files, err := ListFiles(fromPath, includeDirs) 382 if err != nil { 383 return err 384 } 385 386 for _, v := range files { 387 // Skip if excluded 388 if IsStringInSlice(filepath.Base(v), excludeNames) { 389 continue 390 } 391 392 dir, err := IsDirExists(v, false) 393 if err != nil { 394 return err 395 } 396 397 if dir { 398 toPath := toPath + GetFileSeparator() + filepath.Base(v) 399 err := CopyDir(v, toPath, true, nil) 400 if err != nil { 401 return err 402 } 403 continue 404 } 405 err = CopyFile(toPath, v) 406 if err != nil { 407 return err 408 } 409 } 410 return err 411 } 412 413 func IsStringInSlice(string string, strings []string) bool { 414 for _, v := range strings { 415 if v == string { 416 return true 417 } 418 } 419 return false 420 } 421 422 // Removing the provided path from the filesystem 423 func RemovePath(testPath string) error { 424 if _, err := os.Stat(testPath); err == nil { 425 // Delete the path 426 err = os.RemoveAll(testPath) 427 if err != nil { 428 return errors.New("Cannot remove path: " + testPath + " due to: " + err.Error()) 429 } 430 } 431 return nil 432 } 433 434 // Renaming from old path to new path. 435 func RenamePath(oldPath, newPath string) error { 436 err := CopyDir(oldPath, newPath, true, nil) 437 if err != nil { 438 return errors.New("Error copying directory: " + oldPath + "to" + newPath + err.Error()) 439 } 440 RemovePath(oldPath) 441 return nil 442 } 443 444 // Returns the path to the directory in which itemToFind is located. 445 // Traversing through directories from current work-dir to root. 446 // itemType determines whether looking for a file or dir. 447 func FindUpstream(itemToFInd string, itemType ItemType) (wd string, exists bool, err error) { 448 // Create a map to store all paths visited, to avoid running in circles. 449 visitedPaths := make(map[string]bool) 450 // Get the current directory. 451 wd, err = os.Getwd() 452 if err != nil { 453 return 454 } 455 defer os.Chdir(wd) 456 457 // Get the OS root. 458 osRoot := os.Getenv("SYSTEMDRIVE") 459 if osRoot != "" { 460 // If this is a Windows machine: 461 osRoot += "\\" 462 } else { 463 // Unix: 464 osRoot = "/" 465 } 466 467 // Check if the current directory includes itemToFind. If not, check the parent directory 468 // and so on. 469 exists = false 470 for { 471 // If itemToFind is found in the current directory, return the path. 472 if itemType == File { 473 exists, err = IsFileExists(filepath.Join(wd, itemToFInd), false) 474 } else { 475 exists, err = IsDirExists(filepath.Join(wd, itemToFInd), false) 476 } 477 if err != nil || exists { 478 return 479 } 480 481 // If this the OS root, we can stop. 482 if wd == osRoot { 483 break 484 } 485 486 // Save this path. 487 visitedPaths[wd] = true 488 // CD to the parent directory. 489 wd = filepath.Dir(wd) 490 os.Chdir(wd) 491 492 // If we already visited this directory, it means that there's a loop and we can stop. 493 if visitedPaths[wd] { 494 return "", false, nil 495 } 496 } 497 498 return "", false, nil 499 } 500 501 type ItemType string 502 503 // Returns true if the two files have the same MD5 checksum. 504 func FilesIdentical(file1 string, file2 string) (bool, error) { 505 srcDetails, err := GetFileDetails(file1) 506 if err != nil { 507 return false, err 508 } 509 toCompareDetails, err := GetFileDetails(file2) 510 if err != nil { 511 return false, err 512 } 513 return srcDetails.Checksum.Md5 == toCompareDetails.Checksum.Md5, nil 514 } 515 516 // Compares provided Md5 and Sha1 to those of a local file. 517 func IsEqualToLocalFile(localFilePath, md5, sha1 string) (bool, error) { 518 exists, err := IsFileExists(localFilePath, false) 519 if err != nil { 520 return false, err 521 } 522 if !exists { 523 return false, nil 524 } 525 localFileDetails, err := GetFileDetails(localFilePath) 526 if err != nil { 527 return false, err 528 } 529 return localFileDetails.Checksum.Md5 == md5 && localFileDetails.Checksum.Sha1 == sha1, nil 530 }