github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/cmds/core/df/df.go (about) 1 // Copyright 2015-2017 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build !plan9 && !windows 6 7 // df reports details of mounted filesystems. 8 // 9 // Synopsis 10 // 11 // df [-k] [-m] 12 // 13 // Description 14 // 15 // read mount information from /proc/mounts and 16 // statfs syscall and display summary information for all 17 // mount points that have a non-zero block count. 18 // Users can choose to see the diplay in KB or MB. 19 // 20 // Options 21 // 22 // -k: display values in KB (default) 23 // -m: dispaly values in MB 24 package main 25 26 import ( 27 "bytes" 28 "errors" 29 "flag" 30 "fmt" 31 "io" 32 "log" 33 "math" 34 "os" 35 "syscall" 36 ) 37 38 type flags struct { 39 k bool 40 m bool 41 } 42 43 var ( 44 fargs = flags{} 45 units uint64 46 47 errKMExclusiv = errors.New("options -k and -m are mutually exclusive") 48 ) 49 50 func init() { 51 flag.BoolVar(&fargs.k, "k", false, "Express the values in kilobytes (default)") 52 flag.BoolVar(&fargs.m, "m", false, "Express the values in megabytes") 53 } 54 55 const ( 56 // B is Bytes 57 B = 1 58 // KB is kilobytes 59 KB = 1024 * B 60 // MB is megabytes 61 MB = 1024 * KB 62 63 procmountsFile = "/proc/mounts" 64 ) 65 66 // Mount is a structure used to contain mount point data 67 type mount struct { 68 Device string 69 MountPoint string 70 FileSystemType string 71 Flags string 72 Bsize int64 73 Blocks uint64 74 Total uint64 75 Used uint64 76 Avail uint64 77 PCT uint8 78 } 79 80 type mountinfomap map[string]mount 81 82 // mountinfo returns a map of mounts representing 83 // the data in /proc/mounts 84 func mountinfo() (mountinfomap, error) { 85 buf, err := os.ReadFile(procmountsFile) 86 if err != nil { 87 return nil, err 88 } 89 return mountinfoFromBytes(buf) 90 } 91 92 // returns a map generated from the bytestream returned 93 // from /proc/mounts 94 // for tidiness, we decide to ignore filesystems of size 0 95 // to exclude cgroup, procfs and sysfs types 96 func mountinfoFromBytes(buf []byte) (mountinfomap, error) { 97 ret := make(mountinfomap) 98 for _, line := range bytes.Split(buf, []byte{'\n'}) { 99 kv := bytes.SplitN(line, []byte{' '}, 6) 100 if len(kv) != 6 { 101 // can't interpret this 102 continue 103 } 104 key := string(kv[1]) 105 var mnt mount 106 mnt.Device = string(kv[0]) 107 mnt.MountPoint = string(kv[1]) 108 mnt.FileSystemType = string(kv[2]) 109 mnt.Flags = string(kv[3]) 110 if err := diskUsage(&mnt); err != nil { 111 return nil, err 112 } 113 if mnt.Blocks == 0 { 114 continue 115 } else { 116 ret[key] = mnt 117 } 118 } 119 return ret, nil 120 } 121 122 // diskUsage calculates the usage statistics of a mount point 123 // note: arm7 Bsize is int32; all others are int64 124 func diskUsage(mnt *mount) error { 125 fs := syscall.Statfs_t{} 126 if err := syscall.Statfs(mnt.MountPoint, &fs); err != nil { 127 return err 128 } 129 mnt.Blocks = fs.Blocks * uint64(fs.Bsize) / units 130 mnt.Bsize = int64(fs.Bsize) 131 mnt.Total = fs.Blocks * uint64(fs.Bsize) / units 132 mnt.Avail = fs.Bavail * uint64(fs.Bsize) / units 133 mnt.Used = (fs.Blocks - fs.Bfree) * uint64(fs.Bsize) / units 134 pct := float64((fs.Blocks - fs.Bfree)) * 100 / float64(fs.Blocks) 135 mnt.PCT = uint8(math.Ceil(pct)) 136 return nil 137 } 138 139 // setUnits takes the command line flags and configures 140 // the correct units used to calculate display values 141 func setUnits(inKB, inMB bool) error { 142 if inKB && inMB { 143 return errKMExclusiv 144 } 145 if inMB { 146 units = MB 147 } else { 148 units = KB 149 } 150 return nil 151 } 152 153 func printHeader(w io.Writer, blockSize string) { 154 fmt.Fprintf(w, "Filesystem Type %v-blocks Used Available Use%% Mounted on\n", blockSize) 155 } 156 157 func printMount(w io.Writer, mnt mount) { 158 fmt.Fprintf(w, "%-20v %-9v %12v %10v %12v %4v%% %-13v\n", 159 mnt.Device, 160 mnt.FileSystemType, 161 mnt.Blocks, 162 mnt.Used, 163 mnt.Avail, 164 mnt.PCT, 165 mnt.MountPoint) 166 } 167 168 func df(w io.Writer, fargs flags, args []string) error { 169 if err := setUnits(fargs.k, fargs.m); err != nil { 170 return err 171 } 172 mounts, err := mountinfo() 173 if err != nil { 174 return fmt.Errorf("mountinfo()=_,%q, want: _,nil", err) 175 } 176 blocksize := "1K" 177 if fargs.m { 178 blocksize = "1M" 179 } 180 181 if len(args) == 0 { 182 printHeader(w, blocksize) 183 for _, mnt := range mounts { 184 printMount(w, mnt) 185 } 186 187 return nil 188 } 189 190 var fileDevs []uint64 191 for _, arg := range args { 192 fileDev, err := deviceNumber(arg) 193 if err != nil { 194 fmt.Fprintf(os.Stderr, "df: %v\n", err) 195 continue 196 } 197 198 fileDevs = append(fileDevs, fileDev) 199 } 200 201 showHeader := true 202 for _, mnt := range mounts { 203 stDev, err := deviceNumber(mnt.MountPoint) 204 if err != nil { 205 fmt.Fprintf(os.Stderr, "df: %v\n", err) 206 continue 207 } 208 209 for _, fDev := range fileDevs { 210 if fDev == stDev { 211 if showHeader { 212 printHeader(w, blocksize) 213 showHeader = false 214 } 215 printMount(w, mnt) 216 } 217 } 218 } 219 220 return nil 221 } 222 223 func main() { 224 flag.Parse() 225 if err := df(os.Stdout, fargs, flag.Args()); err != nil { 226 log.Fatal(err) 227 } 228 }