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  }