github.com/mondo192/jfrog-client-go@v1.0.0/utils/io/fileutils/files.go (about)

     1  package fileutils
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"encoding/json"
     7  	"errors"
     8  	"golang.org/x/exp/slices"
     9  	"io"
    10  	"net/url"
    11  	"os"
    12  	"os/user"
    13  	"path/filepath"
    14  	"reflect"
    15  	"strings"
    16  
    17  	"github.com/jfrog/build-info-go/entities"
    18  	biutils "github.com/jfrog/build-info-go/utils"
    19  	gofrog "github.com/jfrog/gofrog/io"
    20  
    21  	"github.com/mondo192/jfrog-client-go/utils/errorutils"
    22  )
    23  
    24  const (
    25  	SymlinkFileContent          = ""
    26  	File               ItemType = "file"
    27  	Dir                ItemType = "dir"
    28  	Any                ItemType = "any"
    29  )
    30  
    31  func GetFileSeparator() string {
    32  	return string(os.PathSeparator)
    33  }
    34  
    35  // Check if path exists.
    36  // If path points at a symlink and `preserveSymLink == true`,
    37  // function will return `true` regardless of the symlink target
    38  func IsPathExists(path string, preserveSymLink bool) bool {
    39  	_, err := GetFileInfo(path, preserveSymLink)
    40  	return !os.IsNotExist(err)
    41  }
    42  
    43  // Check if path points at a file.
    44  // If path points at a symlink and `preserveSymLink == true`,
    45  // function will return `true` regardless of the symlink target
    46  func IsFileExists(path string, preserveSymLink bool) (bool, error) {
    47  	fileInfo, err := GetFileInfo(path, preserveSymLink)
    48  	if err != nil {
    49  		if os.IsNotExist(err) { // If doesn't exist, don't omit an error
    50  			return false, nil
    51  		}
    52  		return false, errorutils.CheckError(err)
    53  	}
    54  	return !fileInfo.IsDir(), nil
    55  }
    56  
    57  // Check if path points at a directory.
    58  // If path points at a symlink and `preserveSymLink == true`,
    59  // function will return `false` regardless of the symlink target
    60  func IsDirExists(path string, preserveSymLink bool) (bool, error) {
    61  	fileInfo, err := GetFileInfo(path, preserveSymLink)
    62  	if err != nil {
    63  		if os.IsNotExist(err) { // If doesn't exist, don't omit an error
    64  			return false, nil
    65  		}
    66  		return false, errorutils.CheckError(err)
    67  	}
    68  	return fileInfo.IsDir(), nil
    69  }
    70  
    71  // Get the file info of the file in path.
    72  // If path points at a symlink and `preserveSymLink == true`, return the file info of the symlink instead
    73  func GetFileInfo(path string, preserveSymLink bool) (fileInfo os.FileInfo, err error) {
    74  	if preserveSymLink {
    75  		fileInfo, err = os.Lstat(path)
    76  	} else {
    77  		fileInfo, err = os.Stat(path)
    78  	}
    79  	// We should not do CheckError here, because the error is checked by the calling functions.
    80  	return fileInfo, err
    81  }
    82  
    83  func IsDirEmpty(path string) (isEmpty bool, err error) {
    84  	dir, err := os.Open(path)
    85  	if err != nil {
    86  		return false, errorutils.CheckError(err)
    87  	}
    88  	defer func() {
    89  		e := dir.Close()
    90  		if err == nil {
    91  			err = errorutils.CheckError(e)
    92  		}
    93  	}()
    94  
    95  	_, err = dir.Readdirnames(1)
    96  	if err == io.EOF {
    97  		return true, nil
    98  	}
    99  	return false, errorutils.CheckError(err)
   100  }
   101  
   102  func IsPathSymlink(path string) bool {
   103  	f, _ := os.Lstat(path)
   104  	return f != nil && IsFileSymlink(f)
   105  }
   106  
   107  func IsFileSymlink(file os.FileInfo) bool {
   108  	return file.Mode()&os.ModeSymlink != 0
   109  }
   110  
   111  // Return the file's name and dir of a given path by finding the index of the last separator in the path.
   112  // Support separators : "/" , "\\" and "\\\\"
   113  func GetFileAndDirFromPath(path string) (fileName, dir string) {
   114  	index1 := strings.LastIndex(path, "/")
   115  	index2 := strings.LastIndex(path, "\\")
   116  	var index int
   117  	offset := 0
   118  	if index1 >= index2 {
   119  		index = index1
   120  	} else {
   121  		index = index2
   122  		// Check if the last separator is "\\\\" or "\\".
   123  		index3 := strings.LastIndex(path, "\\\\")
   124  		if index3 != -1 && index2-index3 == 1 {
   125  			offset = 1
   126  		}
   127  	}
   128  	if index != -1 {
   129  		fileName = path[index+1:]
   130  		// If the last separator is "\\\\" index will contain the index of the last "\\" ,
   131  		// to get the dir path (without separator suffix) we will use the offset's value.
   132  		dir = path[:index-offset]
   133  		return
   134  	}
   135  	fileName = path
   136  	dir = ""
   137  	return
   138  }
   139  
   140  // Get the local path and filename from original file name and path according to targetPath
   141  func GetLocalPathAndFile(originalFileName, relativePath, targetPath string, flat bool, placeholdersUsed bool) (localTargetPath, fileName string) {
   142  	targetFileName, targetDirPath := GetFileAndDirFromPath(targetPath)
   143  	// Remove double slashes and double backslashes that may appear in the path
   144  	localTargetPath = filepath.Join(targetDirPath)
   145  	// When placeholders are used, the file path shouldn't be taken into account (or in other words, flat = true).
   146  	if !flat && !placeholdersUsed {
   147  		localTargetPath = filepath.Join(targetDirPath, relativePath)
   148  	}
   149  
   150  	fileName = originalFileName
   151  	// '.' as a target path is equivalent to an empty target path.
   152  	if targetFileName != "" && targetFileName != "." {
   153  		fileName = targetFileName
   154  	}
   155  	return
   156  }
   157  
   158  // Return the recursive list of files and directories in the specified path
   159  func ListFilesRecursiveWalkIntoDirSymlink(path string, walkIntoDirSymlink bool) (fileList []string, err error) {
   160  	fileList = []string{}
   161  	err = gofrog.Walk(path, func(path string, f os.FileInfo, err error) error {
   162  		fileList = append(fileList, path)
   163  		return nil
   164  	}, walkIntoDirSymlink)
   165  	err = errorutils.CheckError(err)
   166  	return
   167  }
   168  
   169  // Return all files in the specified path who satisfy the filter func. Not recursive.
   170  func ListFilesByFilterFunc(path string, filterFunc func(filePath string) (bool, error)) ([]string, error) {
   171  	sep := GetFileSeparator()
   172  	if !strings.HasSuffix(path, sep) {
   173  		path += sep
   174  	}
   175  	var fileList []string
   176  	files, _ := os.ReadDir(path)
   177  	path = strings.TrimPrefix(path, "."+sep)
   178  
   179  	for _, f := range files {
   180  		filePath := path + f.Name()
   181  		satisfy, err := filterFunc(filePath)
   182  		if err != nil {
   183  			return nil, err
   184  		}
   185  		if !satisfy {
   186  			continue
   187  		}
   188  		exists, err := IsFileExists(filePath, false)
   189  		if err != nil {
   190  			return nil, err
   191  		}
   192  		if exists {
   193  			fileList = append(fileList, filePath)
   194  			continue
   195  		}
   196  
   197  		// Checks if the filepath is a symlink.
   198  		if IsPathSymlink(filePath) {
   199  			// Gets the file info of the symlink.
   200  			file, err := GetFileInfo(filePath, false)
   201  			if errorutils.CheckError(err) != nil {
   202  				return nil, err
   203  			}
   204  			// Checks if the symlink is a file.
   205  			if !file.IsDir() {
   206  				fileList = append(fileList, filePath)
   207  			}
   208  		}
   209  	}
   210  	return fileList, nil
   211  }
   212  
   213  // Return the list of files and directories in the specified path
   214  func ListFiles(path string, includeDirs bool) ([]string, error) {
   215  	sep := GetFileSeparator()
   216  	if !strings.HasSuffix(path, sep) {
   217  		path += sep
   218  	}
   219  	fileList := []string{}
   220  	files, _ := os.ReadDir(path)
   221  	path = strings.TrimPrefix(path, "."+sep)
   222  
   223  	for _, f := range files {
   224  		filePath := path + f.Name()
   225  		exists, err := IsFileExists(filePath, false)
   226  		if err != nil {
   227  			return nil, err
   228  		}
   229  		if exists || IsPathSymlink(filePath) {
   230  			fileList = append(fileList, filePath)
   231  		} else if includeDirs {
   232  			isDir, err := IsDirExists(filePath, false)
   233  			if err != nil {
   234  				return nil, err
   235  			}
   236  			if isDir {
   237  				fileList = append(fileList, filePath)
   238  			}
   239  		}
   240  	}
   241  	return fileList, nil
   242  }
   243  
   244  func GetUploadRequestContent(file *os.File) io.Reader {
   245  	if file == nil {
   246  		return bytes.NewBuffer([]byte(SymlinkFileContent))
   247  	}
   248  	return bufio.NewReader(file)
   249  }
   250  
   251  func GetFileSize(file *os.File) (int64, error) {
   252  	size := int64(0)
   253  	if file != nil {
   254  		fileInfo, err := file.Stat()
   255  		if errorutils.CheckError(err) != nil {
   256  			return size, err
   257  		}
   258  		size = fileInfo.Size()
   259  	}
   260  	return size, nil
   261  }
   262  
   263  func CreateFilePath(localPath, fileName string) (string, error) {
   264  	if localPath != "" {
   265  		err := os.MkdirAll(localPath, 0777)
   266  		if errorutils.CheckError(err) != nil {
   267  			return "", err
   268  		}
   269  		fileName = filepath.Join(localPath, fileName)
   270  	}
   271  	return fileName, nil
   272  }
   273  
   274  func CreateDirIfNotExist(path string) error {
   275  	exist, err := IsDirExists(path, false)
   276  	if exist || err != nil {
   277  		return err
   278  	}
   279  	_, err = CreateFilePath(path, "")
   280  	return err
   281  }
   282  
   283  // Reads the content of the file in the source path and appends it to
   284  // the file in the destination path.
   285  func AppendFile(srcPath string, destFile *os.File) error {
   286  	srcFile, err := os.Open(srcPath)
   287  	err = errorutils.CheckError(err)
   288  	if err != nil {
   289  		return err
   290  	}
   291  
   292  	defer func() error {
   293  		err := srcFile.Close()
   294  		return errorutils.CheckError(err)
   295  	}()
   296  
   297  	reader := bufio.NewReader(srcFile)
   298  
   299  	writer := bufio.NewWriter(destFile)
   300  	buf := make([]byte, 1024000)
   301  	for {
   302  		n, err := reader.Read(buf)
   303  		if err != io.EOF {
   304  			err = errorutils.CheckError(err)
   305  			if err != nil {
   306  				return err
   307  			}
   308  		}
   309  		if n == 0 {
   310  			break
   311  		}
   312  		_, err = writer.Write(buf[:n])
   313  		err = errorutils.CheckError(err)
   314  		if err != nil {
   315  			return err
   316  		}
   317  	}
   318  	err = writer.Flush()
   319  	return errorutils.CheckError(err)
   320  }
   321  
   322  func GetHomeDir() string {
   323  	home := os.Getenv("HOME")
   324  	if home != "" {
   325  		return home
   326  	}
   327  	home = os.Getenv("USERPROFILE")
   328  	if home != "" {
   329  		return home
   330  	}
   331  	currentUser, err := user.Current()
   332  	if err == nil {
   333  		return currentUser.HomeDir
   334  	}
   335  	return ""
   336  }
   337  
   338  func IsSshUrl(urlPath string) bool {
   339  	u, err := url.Parse(urlPath)
   340  	if err != nil {
   341  		return false
   342  	}
   343  	return strings.ToLower(u.Scheme) == "ssh"
   344  }
   345  
   346  func ReadFile(filePath string) ([]byte, error) {
   347  	content, err := os.ReadFile(filePath)
   348  	err = errorutils.CheckError(err)
   349  	return content, err
   350  }
   351  
   352  func GetFileDetails(filePath string, includeChecksums bool) (details *FileDetails, err error) {
   353  	details = new(FileDetails)
   354  	if includeChecksums {
   355  		details.Checksum, err = calcChecksumDetails(filePath)
   356  		if err != nil {
   357  			return details, err
   358  		}
   359  	} else {
   360  		details.Checksum = entities.Checksum{}
   361  	}
   362  
   363  	file, err := os.Open(filePath)
   364  	defer func() {
   365  		e := file.Close()
   366  		if err == nil {
   367  			err = errorutils.CheckError(e)
   368  		}
   369  	}()
   370  	if errorutils.CheckError(err) != nil {
   371  		return nil, err
   372  	}
   373  	fileInfo, err := file.Stat()
   374  	if errorutils.CheckError(err) != nil {
   375  		return nil, err
   376  	}
   377  	details.Size = fileInfo.Size()
   378  	return details, nil
   379  }
   380  
   381  func calcChecksumDetails(filePath string) (checksum entities.Checksum, err error) {
   382  	file, err := os.Open(filePath)
   383  	defer func() {
   384  		e := file.Close()
   385  		if err == nil {
   386  			err = errorutils.CheckError(e)
   387  		}
   388  	}()
   389  	if errorutils.CheckError(err) != nil {
   390  		return entities.Checksum{}, err
   391  	}
   392  	return calcChecksumDetailsFromReader(file)
   393  }
   394  
   395  func GetFileDetailsFromReader(reader io.Reader, includeChecksums bool) (*FileDetails, error) {
   396  	var err error
   397  	details := new(FileDetails)
   398  
   399  	pr, pw := io.Pipe()
   400  	defer pr.Close()
   401  
   402  	go func() {
   403  		defer pw.Close()
   404  		details.Size, err = io.Copy(pw, reader)
   405  	}()
   406  
   407  	if includeChecksums {
   408  		details.Checksum, err = calcChecksumDetailsFromReader(pr)
   409  	}
   410  	return details, err
   411  }
   412  
   413  func calcChecksumDetailsFromReader(reader io.Reader) (entities.Checksum, error) {
   414  	checksumInfo, err := biutils.CalcChecksums(reader)
   415  	if err != nil {
   416  		return entities.Checksum{}, errorutils.CheckError(err)
   417  	}
   418  	return entities.Checksum{Md5: checksumInfo[biutils.MD5], Sha1: checksumInfo[biutils.SHA1], Sha256: checksumInfo[biutils.SHA256]}, nil
   419  }
   420  
   421  type FileDetails struct {
   422  	Checksum entities.Checksum
   423  	Size     int64
   424  }
   425  
   426  func CopyFile(dst, src string) (err error) {
   427  	srcFile, err := os.Open(src)
   428  	if err != nil {
   429  		return err
   430  	}
   431  	defer func() {
   432  		e := srcFile.Close()
   433  		if err == nil {
   434  			err = errorutils.CheckError(e)
   435  		}
   436  	}()
   437  	fileName, _ := GetFileAndDirFromPath(src)
   438  	dstPath, err := CreateFilePath(dst, fileName)
   439  	if err != nil {
   440  		return err
   441  	}
   442  	dstFile, err := os.Create(dstPath)
   443  	if err != nil {
   444  		return err
   445  	}
   446  	defer func() {
   447  		e := dstFile.Close()
   448  		if err == nil {
   449  			err = errorutils.CheckError(e)
   450  		}
   451  	}()
   452  	_, err = io.Copy(dstFile, srcFile)
   453  	if err != nil {
   454  		return err
   455  	}
   456  	return nil
   457  }
   458  
   459  // Copy directory content from one path to another.
   460  // includeDirs means to copy also the dirs if presented in the src folder.
   461  // excludeNames - Skip files/dirs in the src folder that match names in provided slice. ONLY excludes first layer (only in src folder).
   462  func CopyDir(fromPath, toPath string, includeDirs bool, excludeNames []string) error {
   463  	err := CreateDirIfNotExist(toPath)
   464  	if err != nil {
   465  		return err
   466  	}
   467  
   468  	files, err := ListFiles(fromPath, includeDirs)
   469  	if err != nil {
   470  		return err
   471  	}
   472  
   473  	for _, v := range files {
   474  		// Skip if excluded
   475  		if slices.Contains(excludeNames, filepath.Base(v)) {
   476  			continue
   477  		}
   478  
   479  		dir, err := IsDirExists(v, false)
   480  		if err != nil {
   481  			return err
   482  		}
   483  
   484  		if dir {
   485  			toPath := toPath + GetFileSeparator() + filepath.Base(v)
   486  			err := CopyDir(v, toPath, true, nil)
   487  			if err != nil {
   488  				return err
   489  			}
   490  			continue
   491  		}
   492  		err = CopyFile(toPath, v)
   493  		if err != nil {
   494  			return err
   495  		}
   496  	}
   497  	return err
   498  }
   499  
   500  // Removing the provided path from the filesystem
   501  func RemovePath(testPath string) error {
   502  	if _, err := os.Stat(testPath); err == nil {
   503  		// Delete the path
   504  		err = RemoveTempDir(testPath)
   505  		if err != nil {
   506  			return errors.New("Cannot remove path: " + testPath + " due to: " + err.Error())
   507  		}
   508  	}
   509  	return nil
   510  }
   511  
   512  // Renaming from old path to new path.
   513  func RenamePath(oldPath, newPath string) error {
   514  	err := CopyDir(oldPath, newPath, true, nil)
   515  	if err != nil {
   516  		return errors.New("Error copying directory: " + oldPath + "to" + newPath + err.Error())
   517  	}
   518  	return RemovePath(oldPath)
   519  }
   520  
   521  // Returns the path to the directory in which itemToFind is located.
   522  // Traversing through directories from current work-dir to root.
   523  // itemType determines whether looking for a file or dir.
   524  func FindUpstream(itemToFInd string, itemType ItemType) (wd string, exists bool, err error) {
   525  	// Create a map to store all paths visited, to avoid running in circles.
   526  	visitedPaths := make(map[string]bool)
   527  	// Get the current directory.
   528  	wd, err = os.Getwd()
   529  	if err != nil {
   530  		return
   531  	}
   532  	defer os.Chdir(wd)
   533  
   534  	// Get the OS root.
   535  	osRoot := os.Getenv("SYSTEMDRIVE")
   536  	if osRoot != "" {
   537  		// If this is a Windows machine:
   538  		osRoot += "\\"
   539  	} else {
   540  		// Unix:
   541  		osRoot = "/"
   542  	}
   543  
   544  	// Check if the current directory includes itemToFind. If not, check the parent directory
   545  	// and so on.
   546  	exists = false
   547  	for {
   548  		// If itemToFind is found in the current directory, return the path.
   549  		switch itemType {
   550  		case Any:
   551  			exists = IsPathExists(filepath.Join(wd, itemToFInd), false)
   552  		case File:
   553  			exists, err = IsFileExists(filepath.Join(wd, itemToFInd), false)
   554  		case Dir:
   555  			exists, err = IsDirExists(filepath.Join(wd, itemToFInd), false)
   556  		}
   557  		if err != nil || exists {
   558  			return
   559  		}
   560  
   561  		// If this the OS root, we can stop.
   562  		if wd == osRoot {
   563  			break
   564  		}
   565  
   566  		// Save this path.
   567  		visitedPaths[wd] = true
   568  		// CD to the parent directory.
   569  		wd = filepath.Dir(wd)
   570  		err := os.Chdir(wd)
   571  		if err != nil {
   572  			return "", false, err
   573  		}
   574  
   575  		// If we already visited this directory, it means that there's a loop and we can stop.
   576  		if visitedPaths[wd] {
   577  			return "", false, nil
   578  		}
   579  	}
   580  
   581  	return "", false, nil
   582  }
   583  
   584  type ItemType string
   585  
   586  // Returns true if the two files have the same MD5 checksum.
   587  func FilesIdentical(file1 string, file2 string) (bool, error) {
   588  	srcDetails, err := GetFileDetails(file1, true)
   589  	if err != nil {
   590  		return false, err
   591  	}
   592  	toCompareDetails, err := GetFileDetails(file2, true)
   593  	if err != nil {
   594  		return false, err
   595  	}
   596  	return srcDetails.Checksum.Md5 == toCompareDetails.Checksum.Md5, nil
   597  }
   598  
   599  // JSONEqual compares the JSON from two files.
   600  func JsonEqual(filePath1, filePath2 string) (isEqual bool, err error) {
   601  	reader1, err := os.Open(filePath1)
   602  	if err != nil {
   603  		return false, err
   604  	}
   605  	defer func() {
   606  		e := reader1.Close()
   607  		if err == nil {
   608  			err = errorutils.CheckError(e)
   609  		}
   610  	}()
   611  	reader2, err := os.Open(filePath2)
   612  	if err != nil {
   613  		return false, err
   614  	}
   615  	defer func() {
   616  		e := reader2.Close()
   617  		if err == nil {
   618  			err = errorutils.CheckError(e)
   619  		}
   620  	}()
   621  	var j, j2 interface{}
   622  	d := json.NewDecoder(reader1)
   623  	if err := d.Decode(&j); err != nil {
   624  		return false, err
   625  	}
   626  	d = json.NewDecoder(reader2)
   627  	if err := d.Decode(&j2); err != nil {
   628  		return false, err
   629  	}
   630  	return reflect.DeepEqual(j2, j), nil
   631  }
   632  
   633  // Compares provided Md5 and Sha1 to those of a local file.
   634  func IsEqualToLocalFile(localFilePath, md5, sha1 string) (bool, error) {
   635  	exists, err := IsFileExists(localFilePath, false)
   636  	if err != nil {
   637  		return false, err
   638  	}
   639  	if !exists {
   640  		return false, nil
   641  	}
   642  	localFileDetails, err := GetFileDetails(localFilePath, true)
   643  	if err != nil {
   644  		return false, err
   645  	}
   646  	return localFileDetails.Checksum.Md5 == md5 && localFileDetails.Checksum.Sha1 == sha1, nil
   647  }
   648  
   649  // Move directory content from one path to another.
   650  func MoveDir(fromPath, toPath string) error {
   651  	err := CreateDirIfNotExist(toPath)
   652  	if err != nil {
   653  		return err
   654  	}
   655  
   656  	files, err := ListFiles(fromPath, true)
   657  	if err != nil {
   658  		return err
   659  	}
   660  
   661  	for _, v := range files {
   662  		dir, err := IsDirExists(v, true)
   663  		if err != nil {
   664  			return err
   665  		}
   666  
   667  		if dir {
   668  			toPath := toPath + GetFileSeparator() + filepath.Base(v)
   669  			err := MoveDir(v, toPath)
   670  			if err != nil {
   671  				return err
   672  			}
   673  			continue
   674  		}
   675  		err = MoveFile(v, filepath.Join(toPath, filepath.Base(v)))
   676  		if err != nil {
   677  			return err
   678  		}
   679  	}
   680  	return err
   681  }
   682  
   683  // GoLang: os.Rename() give error "invalid cross-device link" for Docker container with Volumes.
   684  // MoveFile(source, destination) will work moving file between folders
   685  // Therefore, we are using our own implementation (MoveFile) in order to rename files.
   686  func MoveFile(sourcePath, destPath string) (err error) {
   687  	inputFileOpen := true
   688  	var inputFile *os.File
   689  	inputFile, err = os.Open(sourcePath)
   690  	if err != nil {
   691  		return errorutils.CheckError(err)
   692  	}
   693  	defer func() {
   694  		if inputFileOpen {
   695  			e := inputFile.Close()
   696  			if err == nil {
   697  				err = errorutils.CheckError(e)
   698  			}
   699  		}
   700  	}()
   701  	inputFileInfo, err := inputFile.Stat()
   702  	if err != nil {
   703  		return errorutils.CheckError(err)
   704  	}
   705  
   706  	var outputFile *os.File
   707  	outputFile, err = os.Create(destPath)
   708  	if err != nil {
   709  		return errorutils.CheckError(err)
   710  	}
   711  	defer func() {
   712  		e := outputFile.Close()
   713  		if err == nil {
   714  			err = errorutils.CheckError(e)
   715  		}
   716  	}()
   717  
   718  	_, err = io.Copy(outputFile, inputFile)
   719  	if err != nil {
   720  		return errorutils.CheckError(err)
   721  	}
   722  	err = os.Chmod(destPath, inputFileInfo.Mode())
   723  	if err != nil {
   724  		return errorutils.CheckError(err)
   725  	}
   726  
   727  	// The copy was successful, so now delete the original file
   728  	err = inputFile.Close()
   729  	if err != nil {
   730  		return errorutils.CheckError(err)
   731  	}
   732  	inputFileOpen = false
   733  	err = os.Remove(sourcePath)
   734  	return errorutils.CheckError(err)
   735  }
   736  
   737  // RemoveDirContents removes the contents of the directory, without removing the directory itself.
   738  // If it encounters an error before removing all the files, it stops and returns that error.
   739  func RemoveDirContents(dirPath string) (err error) {
   740  	d, err := os.Open(dirPath)
   741  	if err != nil {
   742  		return errorutils.CheckError(err)
   743  	}
   744  	defer func() {
   745  		e := d.Close()
   746  		if err == nil {
   747  			err = errorutils.CheckError(e)
   748  		}
   749  	}()
   750  	names, err := d.Readdirnames(-1)
   751  	if err != nil {
   752  		return errorutils.CheckError(err)
   753  	}
   754  	for _, name := range names {
   755  		err = os.RemoveAll(filepath.Join(dirPath, name))
   756  		if err != nil {
   757  			return errorutils.CheckError(err)
   758  		}
   759  	}
   760  	return nil
   761  }