github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/ios/fsutils_unix.go (about)

     1  // Package ios is a collection of interfaces to the local storage subsystem;
     2  // the package includes OS-dependent implementations for those interfaces.
     3  /*
     4   * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
     5   */
     6  package ios
     7  
     8  import (
     9  	"fmt"
    10  	"os/exec"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/NVIDIA/aistore/cmn/debug"
    15  	"github.com/NVIDIA/aistore/cmn/nlog"
    16  	"golang.org/x/sys/unix"
    17  )
    18  
    19  func getFSStats(path string) (fsStats unix.Statfs_t, err error) {
    20  	if err = unix.Statfs(path, &fsStats); err != nil {
    21  		nlog.Errorf("failed to statfs %q, err: %v", path, err)
    22  	}
    23  	return
    24  }
    25  
    26  // - on-disk size is sometimes referred to as "apparent size"
    27  // - `withNonDirPrefix` is allowed to match nothing
    28  // - TODO: carefully differentiate FATAL err-s: access perm-s, invalid command-line, executable missing
    29  func executeDU(cmd *exec.Cmd, dirPath string, withNonDirPrefix bool, outputBlockSize uint64) (uint64, error) {
    30  	out, err := cmd.CombinedOutput()
    31  	if err != nil {
    32  		switch {
    33  		case len(out) == 0:
    34  			return 0, fmt.Errorf("du %s: combined output empty, err: %v", dirPath, err)
    35  		default:
    36  			return 0, fmt.Errorf("failed to du %s: %v (%s)", dirPath, err, string(out))
    37  		}
    38  	}
    39  
    40  	lines := strings.Split(string(out), "\n") // on Windows, use instead strings.FieldsFunc('\n' and '\r'), here and elsewhere
    41  	if n := len(lines); n > 8 {
    42  		lines = lines[n-8:]
    43  	}
    44  	// e.g.: "12345   total"
    45  	for i := len(lines) - 1; i >= 0; i-- {
    46  		s := lines[i]
    47  		if strings.HasSuffix(s, "total") && s[0] > '0' && s[0] <= '9' {
    48  			return uint64(_parseTotal(s)) * outputBlockSize, nil
    49  		}
    50  	}
    51  	if !withNonDirPrefix {
    52  		err = fmt.Errorf("failed to parse 'du %s': ...%v", dirPath, lines)
    53  	}
    54  	return 0, err
    55  }
    56  
    57  func _parseTotal(s string) (size int64) {
    58  	var err error
    59  	for i := range len(s) {
    60  		if s[i] < '0' || s[i] > '9' {
    61  			size, err = strconv.ParseInt(s[:i], 10, 64)
    62  			debug.AssertNoErr(err)
    63  			break
    64  		}
    65  	}
    66  	return
    67  }
    68  
    69  func DirFileCount(dirPath string) (int, error) {
    70  	cmd := fmt.Sprintf("find %s -type f | wc -l", dirPath)
    71  	outputBytes, err := exec.Command("/bin/sh", "-c", cmd).Output()
    72  	out := string(outputBytes)
    73  	if err != nil || out == "" {
    74  		return 0, fmt.Errorf("failed to count the number of files in %q: %v", dirPath, err)
    75  	}
    76  	out = strings.TrimSpace(out)
    77  	return strconv.Atoi(out)
    78  }
    79  
    80  func DirSumFileSizes(dirPath string) (uint64, error) {
    81  	cmd := fmt.Sprintf("find %s -type f | xargs wc -c | tail -1", dirPath)
    82  	outputBytes, err := exec.Command("/bin/sh", "-c", cmd).Output()
    83  	out := string(outputBytes)
    84  	if err != nil || out == "" {
    85  		return 0, fmt.Errorf("failed to correctly sum file sizes in %q: %v", dirPath, err)
    86  	}
    87  	i := strings.IndexByte(out, ' ')
    88  	if i < 0 {
    89  		debug.Assertf(out[0] == '0', "failed to sum file sizes in %q: [%s]", dirPath, out)
    90  		return 0, nil
    91  	}
    92  	return strconv.ParseUint(out[:i], 10, 0)
    93  }