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