fs.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767
  1. // Copyright 2014 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // +build linux
  15. // Provides Filesystem Stats
  16. package fs
  17. import (
  18. "bufio"
  19. "fmt"
  20. "io/ioutil"
  21. "os"
  22. "os/exec"
  23. "path"
  24. "path/filepath"
  25. "regexp"
  26. "strconv"
  27. "strings"
  28. "syscall"
  29. "github.com/docker/docker/pkg/mount"
  30. "github.com/google/cadvisor/devicemapper"
  31. "github.com/google/cadvisor/utils"
  32. dockerutil "github.com/google/cadvisor/utils/docker"
  33. zfs "github.com/mistifyio/go-zfs"
  34. "k8s.io/klog"
  35. )
  36. const (
  37. LabelSystemRoot = "root"
  38. LabelDockerImages = "docker-images"
  39. LabelRktImages = "rkt-images"
  40. LabelCrioImages = "crio-images"
  41. )
  42. const (
  43. // The block size in bytes.
  44. statBlockSize uint64 = 512
  45. // The maximum number of `disk usage` tasks that can be running at once.
  46. maxConcurrentOps = 20
  47. )
  48. // A pool for restricting the number of consecutive `du` and `find` tasks running.
  49. var pool = make(chan struct{}, maxConcurrentOps)
  50. func init() {
  51. for i := 0; i < maxConcurrentOps; i++ {
  52. releaseToken()
  53. }
  54. }
  55. func claimToken() {
  56. <-pool
  57. }
  58. func releaseToken() {
  59. pool <- struct{}{}
  60. }
  61. type partition struct {
  62. mountpoint string
  63. major uint
  64. minor uint
  65. fsType string
  66. blockSize uint
  67. }
  68. type RealFsInfo struct {
  69. // Map from block device path to partition information.
  70. partitions map[string]partition
  71. // Map from label to block device path.
  72. // Labels are intent-specific tags that are auto-detected.
  73. labels map[string]string
  74. // Map from mountpoint to mount information.
  75. mounts map[string]*mount.Info
  76. // devicemapper client
  77. dmsetup devicemapper.DmsetupClient
  78. // fsUUIDToDeviceName is a map from the filesystem UUID to its device name.
  79. fsUUIDToDeviceName map[string]string
  80. }
  81. func NewFsInfo(context Context) (FsInfo, error) {
  82. mounts, err := mount.GetMounts(nil)
  83. if err != nil {
  84. return nil, err
  85. }
  86. fsUUIDToDeviceName, err := getFsUUIDToDeviceNameMap()
  87. if err != nil {
  88. // UUID is not always available across different OS distributions.
  89. // Do not fail if there is an error.
  90. klog.Warningf("Failed to get disk UUID mapping, getting disk info by uuid will not work: %v", err)
  91. }
  92. // Avoid devicemapper container mounts - these are tracked by the ThinPoolWatcher
  93. excluded := []string{fmt.Sprintf("%s/devicemapper/mnt", context.Docker.Root)}
  94. fsInfo := &RealFsInfo{
  95. partitions: processMounts(mounts, excluded),
  96. labels: make(map[string]string, 0),
  97. mounts: make(map[string]*mount.Info, 0),
  98. dmsetup: devicemapper.NewDmsetupClient(),
  99. fsUUIDToDeviceName: fsUUIDToDeviceName,
  100. }
  101. for _, mount := range mounts {
  102. fsInfo.mounts[mount.Mountpoint] = mount
  103. }
  104. fsInfo.addRktImagesLabel(context, mounts)
  105. // need to call this before the log line below printing out the partitions, as this function may
  106. // add a "partition" for devicemapper to fsInfo.partitions
  107. fsInfo.addDockerImagesLabel(context, mounts)
  108. fsInfo.addCrioImagesLabel(context, mounts)
  109. klog.V(1).Infof("Filesystem UUIDs: %+v", fsInfo.fsUUIDToDeviceName)
  110. klog.V(1).Infof("Filesystem partitions: %+v", fsInfo.partitions)
  111. fsInfo.addSystemRootLabel(mounts)
  112. return fsInfo, nil
  113. }
  114. // getFsUUIDToDeviceNameMap creates the filesystem uuid to device name map
  115. // using the information in /dev/disk/by-uuid. If the directory does not exist,
  116. // this function will return an empty map.
  117. func getFsUUIDToDeviceNameMap() (map[string]string, error) {
  118. const dir = "/dev/disk/by-uuid"
  119. if _, err := os.Stat(dir); os.IsNotExist(err) {
  120. return make(map[string]string), nil
  121. }
  122. files, err := ioutil.ReadDir(dir)
  123. if err != nil {
  124. return nil, err
  125. }
  126. fsUUIDToDeviceName := make(map[string]string)
  127. for _, file := range files {
  128. path := filepath.Join(dir, file.Name())
  129. target, err := os.Readlink(path)
  130. if err != nil {
  131. klog.Warningf("Failed to resolve symlink for %q", path)
  132. continue
  133. }
  134. device, err := filepath.Abs(filepath.Join(dir, target))
  135. if err != nil {
  136. return nil, fmt.Errorf("failed to resolve the absolute path of %q", filepath.Join(dir, target))
  137. }
  138. fsUUIDToDeviceName[file.Name()] = device
  139. }
  140. return fsUUIDToDeviceName, nil
  141. }
  142. func processMounts(mounts []*mount.Info, excludedMountpointPrefixes []string) map[string]partition {
  143. partitions := make(map[string]partition, 0)
  144. supportedFsType := map[string]bool{
  145. // all ext systems are checked through prefix.
  146. "btrfs": true,
  147. "tmpfs": true,
  148. "xfs": true,
  149. "zfs": true,
  150. }
  151. for _, mount := range mounts {
  152. if !strings.HasPrefix(mount.Fstype, "ext") && !supportedFsType[mount.Fstype] {
  153. continue
  154. }
  155. // Avoid bind mounts.
  156. if _, ok := partitions[mount.Source]; ok {
  157. continue
  158. }
  159. hasPrefix := false
  160. for _, prefix := range excludedMountpointPrefixes {
  161. if strings.HasPrefix(mount.Mountpoint, prefix) {
  162. hasPrefix = true
  163. break
  164. }
  165. }
  166. if hasPrefix {
  167. continue
  168. }
  169. // btrfs fix: following workaround fixes wrong btrfs Major and Minor Ids reported in /proc/self/mountinfo.
  170. // instead of using values from /proc/self/mountinfo we use stat to get Ids from btrfs mount point
  171. if mount.Fstype == "btrfs" && mount.Major == 0 && strings.HasPrefix(mount.Source, "/dev/") {
  172. major, minor, err := getBtrfsMajorMinorIds(mount)
  173. if err != nil {
  174. klog.Warningf("%s", err)
  175. } else {
  176. mount.Major = major
  177. mount.Minor = minor
  178. }
  179. }
  180. partitions[mount.Source] = partition{
  181. fsType: mount.Fstype,
  182. mountpoint: mount.Mountpoint,
  183. major: uint(mount.Major),
  184. minor: uint(mount.Minor),
  185. }
  186. }
  187. return partitions
  188. }
  189. // getDockerDeviceMapperInfo returns information about the devicemapper device and "partition" if
  190. // docker is using devicemapper for its storage driver. If a loopback device is being used, don't
  191. // return any information or error, as we want to report based on the actual partition where the
  192. // loopback file resides, inside of the loopback file itself.
  193. func (self *RealFsInfo) getDockerDeviceMapperInfo(context DockerContext) (string, *partition, error) {
  194. if context.Driver != DeviceMapper.String() {
  195. return "", nil, nil
  196. }
  197. dataLoopFile := context.DriverStatus[dockerutil.DriverStatusDataLoopFile]
  198. if len(dataLoopFile) > 0 {
  199. return "", nil, nil
  200. }
  201. dev, major, minor, blockSize, err := dockerDMDevice(context.DriverStatus, self.dmsetup)
  202. if err != nil {
  203. return "", nil, err
  204. }
  205. return dev, &partition{
  206. fsType: DeviceMapper.String(),
  207. major: major,
  208. minor: minor,
  209. blockSize: blockSize,
  210. }, nil
  211. }
  212. // addSystemRootLabel attempts to determine which device contains the mount for /.
  213. func (self *RealFsInfo) addSystemRootLabel(mounts []*mount.Info) {
  214. for _, m := range mounts {
  215. if m.Mountpoint == "/" {
  216. self.partitions[m.Source] = partition{
  217. fsType: m.Fstype,
  218. mountpoint: m.Mountpoint,
  219. major: uint(m.Major),
  220. minor: uint(m.Minor),
  221. }
  222. self.labels[LabelSystemRoot] = m.Source
  223. return
  224. }
  225. }
  226. }
  227. // addDockerImagesLabel attempts to determine which device contains the mount for docker images.
  228. func (self *RealFsInfo) addDockerImagesLabel(context Context, mounts []*mount.Info) {
  229. dockerDev, dockerPartition, err := self.getDockerDeviceMapperInfo(context.Docker)
  230. if err != nil {
  231. klog.Warningf("Could not get Docker devicemapper device: %v", err)
  232. }
  233. if len(dockerDev) > 0 && dockerPartition != nil {
  234. self.partitions[dockerDev] = *dockerPartition
  235. self.labels[LabelDockerImages] = dockerDev
  236. } else {
  237. self.updateContainerImagesPath(LabelDockerImages, mounts, getDockerImagePaths(context))
  238. }
  239. }
  240. func (self *RealFsInfo) addCrioImagesLabel(context Context, mounts []*mount.Info) {
  241. if context.Crio.Root != "" {
  242. crioPath := context.Crio.Root
  243. crioImagePaths := map[string]struct{}{
  244. "/": {},
  245. }
  246. for _, dir := range []string{"overlay", "overlay2"} {
  247. crioImagePaths[path.Join(crioPath, dir+"-images")] = struct{}{}
  248. }
  249. for crioPath != "/" && crioPath != "." {
  250. crioImagePaths[crioPath] = struct{}{}
  251. crioPath = filepath.Dir(crioPath)
  252. }
  253. self.updateContainerImagesPath(LabelCrioImages, mounts, crioImagePaths)
  254. }
  255. }
  256. func (self *RealFsInfo) addRktImagesLabel(context Context, mounts []*mount.Info) {
  257. if context.RktPath != "" {
  258. rktPath := context.RktPath
  259. rktImagesPaths := map[string]struct{}{
  260. "/": {},
  261. }
  262. for rktPath != "/" && rktPath != "." {
  263. rktImagesPaths[rktPath] = struct{}{}
  264. rktPath = filepath.Dir(rktPath)
  265. }
  266. self.updateContainerImagesPath(LabelRktImages, mounts, rktImagesPaths)
  267. }
  268. }
  269. // Generate a list of possible mount points for docker image management from the docker root directory.
  270. // Right now, we look for each type of supported graph driver directories, but we can do better by parsing
  271. // some of the context from `docker info`.
  272. func getDockerImagePaths(context Context) map[string]struct{} {
  273. dockerImagePaths := map[string]struct{}{
  274. "/": {},
  275. }
  276. // TODO(rjnagal): Detect docker root and graphdriver directories from docker info.
  277. dockerRoot := context.Docker.Root
  278. for _, dir := range []string{"devicemapper", "btrfs", "aufs", "overlay", "overlay2", "zfs"} {
  279. dockerImagePaths[path.Join(dockerRoot, dir)] = struct{}{}
  280. }
  281. for dockerRoot != "/" && dockerRoot != "." {
  282. dockerImagePaths[dockerRoot] = struct{}{}
  283. dockerRoot = filepath.Dir(dockerRoot)
  284. }
  285. return dockerImagePaths
  286. }
  287. // This method compares the mountpoints with possible container image mount points. If a match is found,
  288. // the label is added to the partition.
  289. func (self *RealFsInfo) updateContainerImagesPath(label string, mounts []*mount.Info, containerImagePaths map[string]struct{}) {
  290. var useMount *mount.Info
  291. for _, m := range mounts {
  292. if _, ok := containerImagePaths[m.Mountpoint]; ok {
  293. if useMount == nil || (len(useMount.Mountpoint) < len(m.Mountpoint)) {
  294. useMount = m
  295. }
  296. }
  297. }
  298. if useMount != nil {
  299. self.partitions[useMount.Source] = partition{
  300. fsType: useMount.Fstype,
  301. mountpoint: useMount.Mountpoint,
  302. major: uint(useMount.Major),
  303. minor: uint(useMount.Minor),
  304. }
  305. self.labels[label] = useMount.Source
  306. }
  307. }
  308. func (self *RealFsInfo) GetDeviceForLabel(label string) (string, error) {
  309. dev, ok := self.labels[label]
  310. if !ok {
  311. return "", fmt.Errorf("non-existent label %q", label)
  312. }
  313. return dev, nil
  314. }
  315. func (self *RealFsInfo) GetLabelsForDevice(device string) ([]string, error) {
  316. labels := []string{}
  317. for label, dev := range self.labels {
  318. if dev == device {
  319. labels = append(labels, label)
  320. }
  321. }
  322. return labels, nil
  323. }
  324. func (self *RealFsInfo) GetMountpointForDevice(dev string) (string, error) {
  325. p, ok := self.partitions[dev]
  326. if !ok {
  327. return "", fmt.Errorf("no partition info for device %q", dev)
  328. }
  329. return p.mountpoint, nil
  330. }
  331. func (self *RealFsInfo) GetFsInfoForPath(mountSet map[string]struct{}) ([]Fs, error) {
  332. filesystems := make([]Fs, 0)
  333. deviceSet := make(map[string]struct{})
  334. diskStatsMap, err := getDiskStatsMap("/proc/diskstats")
  335. if err != nil {
  336. return nil, err
  337. }
  338. for device, partition := range self.partitions {
  339. _, hasMount := mountSet[partition.mountpoint]
  340. _, hasDevice := deviceSet[device]
  341. if mountSet == nil || (hasMount && !hasDevice) {
  342. var (
  343. err error
  344. fs Fs
  345. )
  346. switch partition.fsType {
  347. case DeviceMapper.String():
  348. fs.Capacity, fs.Free, fs.Available, err = getDMStats(device, partition.blockSize)
  349. klog.V(5).Infof("got devicemapper fs capacity stats: capacity: %v free: %v available: %v:", fs.Capacity, fs.Free, fs.Available)
  350. fs.Type = DeviceMapper
  351. case ZFS.String():
  352. if _, devzfs := os.Stat("/dev/zfs"); os.IsExist(devzfs) {
  353. fs.Capacity, fs.Free, fs.Available, err = getZfstats(device)
  354. fs.Type = ZFS
  355. break
  356. }
  357. // if /dev/zfs is not present default to VFS
  358. fallthrough
  359. default:
  360. var inodes, inodesFree uint64
  361. if utils.FileExists(partition.mountpoint) {
  362. fs.Capacity, fs.Free, fs.Available, inodes, inodesFree, err = getVfsStats(partition.mountpoint)
  363. fs.Inodes = &inodes
  364. fs.InodesFree = &inodesFree
  365. fs.Type = VFS
  366. } else {
  367. klog.V(4).Infof("unable to determine file system type, partition mountpoint does not exist: %v", partition.mountpoint)
  368. }
  369. }
  370. if err != nil {
  371. klog.V(4).Infof("Stat fs failed. Error: %v", err)
  372. } else {
  373. deviceSet[device] = struct{}{}
  374. fs.DeviceInfo = DeviceInfo{
  375. Device: device,
  376. Major: uint(partition.major),
  377. Minor: uint(partition.minor),
  378. }
  379. fs.DiskStats = diskStatsMap[device]
  380. filesystems = append(filesystems, fs)
  381. }
  382. }
  383. }
  384. return filesystems, nil
  385. }
  386. var partitionRegex = regexp.MustCompile(`^(?:(?:s|v|xv)d[a-z]+\d*|dm-\d+)$`)
  387. func getDiskStatsMap(diskStatsFile string) (map[string]DiskStats, error) {
  388. diskStatsMap := make(map[string]DiskStats)
  389. file, err := os.Open(diskStatsFile)
  390. if err != nil {
  391. if os.IsNotExist(err) {
  392. klog.Warningf("Not collecting filesystem statistics because file %q was not found", diskStatsFile)
  393. return diskStatsMap, nil
  394. }
  395. return nil, err
  396. }
  397. defer file.Close()
  398. scanner := bufio.NewScanner(file)
  399. for scanner.Scan() {
  400. line := scanner.Text()
  401. words := strings.Fields(line)
  402. if !partitionRegex.MatchString(words[2]) {
  403. continue
  404. }
  405. // 8 50 sdd2 40 0 280 223 7 0 22 108 0 330 330
  406. deviceName := path.Join("/dev", words[2])
  407. wordLength := len(words)
  408. offset := 3
  409. var stats = make([]uint64, wordLength-offset)
  410. if len(stats) < 11 {
  411. return nil, fmt.Errorf("could not parse all 11 columns of /proc/diskstats")
  412. }
  413. var error error
  414. for i := offset; i < wordLength; i++ {
  415. stats[i-offset], error = strconv.ParseUint(words[i], 10, 64)
  416. if error != nil {
  417. return nil, error
  418. }
  419. }
  420. diskStats := DiskStats{
  421. ReadsCompleted: stats[0],
  422. ReadsMerged: stats[1],
  423. SectorsRead: stats[2],
  424. ReadTime: stats[3],
  425. WritesCompleted: stats[4],
  426. WritesMerged: stats[5],
  427. SectorsWritten: stats[6],
  428. WriteTime: stats[7],
  429. IoInProgress: stats[8],
  430. IoTime: stats[9],
  431. WeightedIoTime: stats[10],
  432. }
  433. diskStatsMap[deviceName] = diskStats
  434. }
  435. return diskStatsMap, nil
  436. }
  437. func (self *RealFsInfo) GetGlobalFsInfo() ([]Fs, error) {
  438. return self.GetFsInfoForPath(nil)
  439. }
  440. func major(devNumber uint64) uint {
  441. return uint((devNumber >> 8) & 0xfff)
  442. }
  443. func minor(devNumber uint64) uint {
  444. return uint((devNumber & 0xff) | ((devNumber >> 12) & 0xfff00))
  445. }
  446. func (self *RealFsInfo) GetDeviceInfoByFsUUID(uuid string) (*DeviceInfo, error) {
  447. deviceName, found := self.fsUUIDToDeviceName[uuid]
  448. if !found {
  449. return nil, ErrNoSuchDevice
  450. }
  451. p, found := self.partitions[deviceName]
  452. if !found {
  453. return nil, fmt.Errorf("cannot find device %q in partitions", deviceName)
  454. }
  455. return &DeviceInfo{deviceName, p.major, p.minor}, nil
  456. }
  457. func (self *RealFsInfo) GetDirFsDevice(dir string) (*DeviceInfo, error) {
  458. buf := new(syscall.Stat_t)
  459. err := syscall.Stat(dir, buf)
  460. if err != nil {
  461. return nil, fmt.Errorf("stat failed on %s with error: %s", dir, err)
  462. }
  463. major := major(buf.Dev)
  464. minor := minor(buf.Dev)
  465. for device, partition := range self.partitions {
  466. if partition.major == major && partition.minor == minor {
  467. return &DeviceInfo{device, major, minor}, nil
  468. }
  469. }
  470. mount, found := self.mounts[dir]
  471. // try the parent dir if not found until we reach the root dir
  472. // this is an issue on btrfs systems where the directory is not
  473. // the subvolume
  474. for !found {
  475. pathdir, _ := filepath.Split(dir)
  476. // break when we reach root
  477. if pathdir == "/" {
  478. break
  479. }
  480. // trim "/" from the new parent path otherwise the next possible
  481. // filepath.Split in the loop will not split the string any further
  482. dir = strings.TrimSuffix(pathdir, "/")
  483. mount, found = self.mounts[dir]
  484. }
  485. if found && mount.Fstype == "btrfs" && mount.Major == 0 && strings.HasPrefix(mount.Source, "/dev/") {
  486. major, minor, err := getBtrfsMajorMinorIds(mount)
  487. if err != nil {
  488. klog.Warningf("%s", err)
  489. } else {
  490. return &DeviceInfo{mount.Source, uint(major), uint(minor)}, nil
  491. }
  492. }
  493. return nil, fmt.Errorf("could not find device with major: %d, minor: %d in cached partitions map", major, minor)
  494. }
  495. func GetDirUsage(dir string) (UsageInfo, error) {
  496. var usage UsageInfo
  497. if dir == "" {
  498. return usage, fmt.Errorf("invalid directory")
  499. }
  500. rootInfo, err := os.Stat(dir)
  501. if err != nil {
  502. return usage, fmt.Errorf("could not stat %q to get inode usage: %v", dir, err)
  503. }
  504. rootStat, ok := rootInfo.Sys().(*syscall.Stat_t)
  505. if !ok {
  506. return usage, fmt.Errorf("unsuported fileinfo for getting inode usage of %q", dir)
  507. }
  508. rootDevId := rootStat.Dev
  509. // dedupedInode stores inodes that could be duplicates (nlink > 1)
  510. dedupedInodes := make(map[uint64]struct{})
  511. err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
  512. if os.IsNotExist(err) {
  513. // expected if files appear/vanish
  514. return nil
  515. }
  516. if err != nil {
  517. return fmt.Errorf("unable to count inodes for part of dir %s: %s", dir, err)
  518. }
  519. // according to the docs, Sys can be nil
  520. if info.Sys() == nil {
  521. return fmt.Errorf("fileinfo Sys is nil")
  522. }
  523. s, ok := info.Sys().(*syscall.Stat_t)
  524. if !ok {
  525. return fmt.Errorf("unsupported fileinfo; could not convert to stat_t")
  526. }
  527. if s.Dev != rootDevId {
  528. // don't descend into directories on other devices
  529. return filepath.SkipDir
  530. }
  531. if s.Nlink > 1 {
  532. if _, ok := dedupedInodes[s.Ino]; !ok {
  533. // Dedupe things that could be hardlinks
  534. dedupedInodes[s.Ino] = struct{}{}
  535. usage.Bytes += uint64(s.Blocks) * statBlockSize
  536. usage.Inodes++
  537. }
  538. } else {
  539. usage.Bytes += uint64(s.Blocks) * statBlockSize
  540. usage.Inodes++
  541. }
  542. return nil
  543. })
  544. return usage, nil
  545. }
  546. func (self *RealFsInfo) GetDirUsage(dir string) (UsageInfo, error) {
  547. claimToken()
  548. defer releaseToken()
  549. return GetDirUsage(dir)
  550. }
  551. func getVfsStats(path string) (total uint64, free uint64, avail uint64, inodes uint64, inodesFree uint64, err error) {
  552. var s syscall.Statfs_t
  553. if err = syscall.Statfs(path, &s); err != nil {
  554. return 0, 0, 0, 0, 0, err
  555. }
  556. total = uint64(s.Frsize) * s.Blocks
  557. free = uint64(s.Frsize) * s.Bfree
  558. avail = uint64(s.Frsize) * s.Bavail
  559. inodes = uint64(s.Files)
  560. inodesFree = uint64(s.Ffree)
  561. return total, free, avail, inodes, inodesFree, nil
  562. }
  563. // Devicemapper thin provisioning is detailed at
  564. // https://www.kernel.org/doc/Documentation/device-mapper/thin-provisioning.txt
  565. func dockerDMDevice(driverStatus map[string]string, dmsetup devicemapper.DmsetupClient) (string, uint, uint, uint, error) {
  566. poolName, ok := driverStatus[dockerutil.DriverStatusPoolName]
  567. if !ok || len(poolName) == 0 {
  568. return "", 0, 0, 0, fmt.Errorf("Could not get dm pool name")
  569. }
  570. out, err := dmsetup.Table(poolName)
  571. if err != nil {
  572. return "", 0, 0, 0, err
  573. }
  574. major, minor, dataBlkSize, err := parseDMTable(string(out))
  575. if err != nil {
  576. return "", 0, 0, 0, err
  577. }
  578. return poolName, major, minor, dataBlkSize, nil
  579. }
  580. // parseDMTable parses a single line of `dmsetup table` output and returns the
  581. // major device, minor device, block size, and an error.
  582. func parseDMTable(dmTable string) (uint, uint, uint, error) {
  583. dmTable = strings.Replace(dmTable, ":", " ", -1)
  584. dmFields := strings.Fields(dmTable)
  585. if len(dmFields) < 8 {
  586. return 0, 0, 0, fmt.Errorf("Invalid dmsetup status output: %s", dmTable)
  587. }
  588. major, err := strconv.ParseUint(dmFields[5], 10, 32)
  589. if err != nil {
  590. return 0, 0, 0, err
  591. }
  592. minor, err := strconv.ParseUint(dmFields[6], 10, 32)
  593. if err != nil {
  594. return 0, 0, 0, err
  595. }
  596. dataBlkSize, err := strconv.ParseUint(dmFields[7], 10, 32)
  597. if err != nil {
  598. return 0, 0, 0, err
  599. }
  600. return uint(major), uint(minor), uint(dataBlkSize), nil
  601. }
  602. func getDMStats(poolName string, dataBlkSize uint) (uint64, uint64, uint64, error) {
  603. out, err := exec.Command("dmsetup", "status", poolName).Output()
  604. if err != nil {
  605. return 0, 0, 0, err
  606. }
  607. used, total, err := parseDMStatus(string(out))
  608. if err != nil {
  609. return 0, 0, 0, err
  610. }
  611. used *= 512 * uint64(dataBlkSize)
  612. total *= 512 * uint64(dataBlkSize)
  613. free := total - used
  614. return total, free, free, nil
  615. }
  616. func parseDMStatus(dmStatus string) (uint64, uint64, error) {
  617. dmStatus = strings.Replace(dmStatus, "/", " ", -1)
  618. dmFields := strings.Fields(dmStatus)
  619. if len(dmFields) < 8 {
  620. return 0, 0, fmt.Errorf("Invalid dmsetup status output: %s", dmStatus)
  621. }
  622. used, err := strconv.ParseUint(dmFields[6], 10, 64)
  623. if err != nil {
  624. return 0, 0, err
  625. }
  626. total, err := strconv.ParseUint(dmFields[7], 10, 64)
  627. if err != nil {
  628. return 0, 0, err
  629. }
  630. return used, total, nil
  631. }
  632. // getZfstats returns ZFS mount stats using zfsutils
  633. func getZfstats(poolName string) (uint64, uint64, uint64, error) {
  634. dataset, err := zfs.GetDataset(poolName)
  635. if err != nil {
  636. return 0, 0, 0, err
  637. }
  638. total := dataset.Used + dataset.Avail + dataset.Usedbydataset
  639. return total, dataset.Avail, dataset.Avail, nil
  640. }
  641. // Simple io.Writer implementation that counts how many bytes were written.
  642. type byteCounter struct{ bytesWritten uint64 }
  643. func (b *byteCounter) Write(p []byte) (int, error) {
  644. b.bytesWritten += uint64(len(p))
  645. return len(p), nil
  646. }
  647. // Get major and minor Ids for a mount point using btrfs as filesystem.
  648. func getBtrfsMajorMinorIds(mount *mount.Info) (int, int, error) {
  649. // btrfs fix: following workaround fixes wrong btrfs Major and Minor Ids reported in /proc/self/mountinfo.
  650. // instead of using values from /proc/self/mountinfo we use stat to get Ids from btrfs mount point
  651. buf := new(syscall.Stat_t)
  652. err := syscall.Stat(mount.Source, buf)
  653. if err != nil {
  654. err = fmt.Errorf("stat failed on %s with error: %s", mount.Source, err)
  655. return 0, 0, err
  656. }
  657. klog.V(4).Infof("btrfs mount %#v", mount)
  658. if buf.Mode&syscall.S_IFMT == syscall.S_IFBLK {
  659. err := syscall.Stat(mount.Mountpoint, buf)
  660. if err != nil {
  661. err = fmt.Errorf("stat failed on %s with error: %s", mount.Mountpoint, err)
  662. return 0, 0, err
  663. }
  664. klog.V(4).Infof("btrfs dev major:minor %d:%d\n", int(major(buf.Dev)), int(minor(buf.Dev)))
  665. klog.V(4).Infof("btrfs rdev major:minor %d:%d\n", int(major(buf.Rdev)), int(minor(buf.Rdev)))
  666. return int(major(buf.Dev)), int(minor(buf.Dev)), nil
  667. } else {
  668. return 0, 0, fmt.Errorf("%s is not a block device", mount.Source)
  669. }
  670. }