...

Source file src/k8s.io/kubernetes/pkg/volume/util/fs/fs.go

Documentation: k8s.io/kubernetes/pkg/volume/util/fs

     1  //go:build linux || darwin
     2  // +build linux darwin
     3  
     4  /*
     5  Copyright 2014 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package fs
    21  
    22  import (
    23  	"fmt"
    24  	"os"
    25  	"path/filepath"
    26  	"syscall"
    27  	"time"
    28  
    29  	"golang.org/x/sys/unix"
    30  
    31  	servermetrics "k8s.io/kubernetes/pkg/kubelet/server/metrics"
    32  	"k8s.io/kubernetes/pkg/volume/util/fsquota"
    33  )
    34  
    35  type UsageInfo struct {
    36  	Bytes  int64
    37  	Inodes int64
    38  }
    39  
    40  // Info linux returns (available bytes, byte capacity, byte usage, total inodes, inodes free, inode usage, error)
    41  // for the filesystem that path resides upon.
    42  func Info(path string) (int64, int64, int64, int64, int64, int64, error) {
    43  	statfs := &unix.Statfs_t{}
    44  	err := unix.Statfs(path, statfs)
    45  	if err != nil {
    46  		return 0, 0, 0, 0, 0, 0, err
    47  	}
    48  
    49  	// Available is blocks available * fragment size
    50  	available := int64(statfs.Bavail) * int64(statfs.Bsize)
    51  
    52  	// Capacity is total block count * fragment size
    53  	capacity := int64(statfs.Blocks) * int64(statfs.Bsize)
    54  
    55  	// Usage is block being used * fragment size (aka block size).
    56  	usage := (int64(statfs.Blocks) - int64(statfs.Bfree)) * int64(statfs.Bsize)
    57  
    58  	inodes := int64(statfs.Files)
    59  	inodesFree := int64(statfs.Ffree)
    60  	inodesUsed := inodes - inodesFree
    61  
    62  	return available, capacity, usage, inodes, inodesFree, inodesUsed, nil
    63  }
    64  
    65  // DiskUsage calculates the number of inodes and disk usage for a given directory
    66  func DiskUsage(path string) (UsageInfo, error) {
    67  	var usage UsageInfo
    68  
    69  	if path == "" {
    70  		return usage, fmt.Errorf("invalid directory")
    71  	}
    72  
    73  	// First check whether the quota system knows about this directory
    74  	// A nil quantity or error means that the path does not support quotas
    75  	// or xfs_quota tool is missing and we should use other mechanisms.
    76  	startTime := time.Now()
    77  	consumption, _ := fsquota.GetConsumption(path)
    78  	if consumption != nil {
    79  		usage.Bytes = consumption.Value()
    80  		defer servermetrics.CollectVolumeStatCalDuration("fsquota", startTime)
    81  	} else {
    82  		defer servermetrics.CollectVolumeStatCalDuration("du", startTime)
    83  	}
    84  
    85  	inodes, _ := fsquota.GetInodes(path)
    86  	if inodes != nil {
    87  		usage.Inodes = inodes.Value()
    88  	}
    89  
    90  	if inodes != nil && consumption != nil {
    91  		return usage, nil
    92  	}
    93  
    94  	topLevelStat := &unix.Stat_t{}
    95  	err := unix.Stat(path, topLevelStat)
    96  	if err != nil {
    97  		return usage, err
    98  	}
    99  
   100  	// dedupedInode stores inodes that could be duplicates (nlink > 1)
   101  	dedupedInodes := make(map[uint64]struct{})
   102  
   103  	err = filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
   104  		// ignore files that have been deleted after directory was read
   105  		if os.IsNotExist(err) {
   106  			return nil
   107  		}
   108  		if err != nil {
   109  			return fmt.Errorf("unable to count inodes for %s: %s", path, err)
   110  		}
   111  
   112  		// according to the docs, Sys can be nil
   113  		if info.Sys() == nil {
   114  			return fmt.Errorf("fileinfo Sys is nil")
   115  		}
   116  
   117  		s, ok := info.Sys().(*syscall.Stat_t)
   118  		if !ok {
   119  			return fmt.Errorf("unsupported fileinfo; could not convert to stat_t")
   120  		}
   121  
   122  		if s.Dev != topLevelStat.Dev {
   123  			// don't descend into directories on other devices
   124  			return filepath.SkipDir
   125  		}
   126  
   127  		// Dedupe hardlinks
   128  		if s.Nlink > 1 {
   129  			if _, ok := dedupedInodes[s.Ino]; !ok {
   130  				dedupedInodes[s.Ino] = struct{}{}
   131  			} else {
   132  				return nil
   133  			}
   134  		}
   135  
   136  		if consumption == nil {
   137  			usage.Bytes += int64(s.Blocks) * int64(512) // blocksize in bytes
   138  		}
   139  
   140  		if inodes == nil {
   141  			usage.Inodes++
   142  		}
   143  
   144  		return nil
   145  	})
   146  
   147  	return usage, err
   148  }
   149  

View as plain text