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  }