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 }