Repository: prometheus/procfs
Pull request #89 (2018-04-18 13:11:33)

Update ttar and fix some typos
Hi @grobie,

I was trying to make [codespell](https://github.com/lucasdemarchi/codespell) happy, and got it working. See,
```
$ codespell -S .git*,fixture*
```
We could add this as a **CircleCI** job (node_exporter is using it right now), of course if you agree!

Thanks!

PS: The `nd => netDev` renaming action is because *codespell* identify it as `nd  ==> and, 2nd`, and renaming it is cheaper than excluding `nd`.


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 0000000..6484242
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,53 @@
+---
+version: 2.1
+
+commands:
+  default:
+    steps:
+    - run: make style check_license test lint
+  no-test:
+    steps:
+    - run: make style check_license lint
+
+jobs:
+  test:
+    parameters:
+      command:
+        type: string
+      go_version:
+        type: string
+      os:
+        type: string
+    docker:
+    - image: circleci/golang:<< parameters.go_version >>
+    environment:
+      GOOS: "<< parameters.os >>"
+    working_directory: /go/src/github.com/prometheus/procfs
+    steps:
+    - checkout
+    - << parameters.command >>
+
+workflows:
+  version: 2
+  procfs:
+    jobs:
+    - test:
+        command: default
+        name: linux-1-11
+        os: linux
+        go_version: "1.11"
+    - test:
+        command: default
+        name: linux-1-12
+        os: linux
+        go_version: "1.12"
+    - test:
+        command: no-test
+        name: windows-1-11
+        os: windows
+        go_version: "1.11"
+    - test:
+        command: no-test
+        name: windows-1-12
+        os: windows
+        go_version: "1.12"
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000..438ca92
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,6 @@
+# Run only staticcheck for now. Additional linters will be enabled one-by-one.
+linters:
+  enable:
+  - staticcheck
+  - govet
+  disable-all: true
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 5416cf8..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-sudo: false
-
-language: go
-
-go:
-- 1.7.x
-- 1.8.x
-- 1.9.x
-- 1.10.x
-- 1.x
-
-go_import_path: github.com/prometheus/procfs
-
-script:
-- make style check_license vet test staticcheck
diff --git a/MAINTAINERS.md b/MAINTAINERS.md
index 35993c4..56ba67d 100644
--- a/MAINTAINERS.md
+++ b/MAINTAINERS.md
@@ -1,1 +1,2 @@
-* Tobias Schmidt <tobidt@gmail.com>
+* Johannes 'fish' Ziemke <github@freigeist.org> @discordianfish
+* Paul Gier <pgier@redhat.com> @pgier
diff --git a/Makefile b/Makefile
index 5c8f726..616a0d2 100644
--- a/Makefile
+++ b/Makefile
@@ -11,61 +11,19 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Ensure GOBIN is not set during build so that promu is installed to the correct path
-unexport GOBIN
-
-GO           ?= go
-GOFMT        ?= $(GO)fmt
-FIRST_GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH)))
-STATICCHECK  := $(FIRST_GOPATH)/bin/staticcheck
-pkgs          = $(shell $(GO) list ./... | grep -v /vendor/)
-
-PREFIX                  ?= $(shell pwd)
-BIN_DIR                 ?= $(shell pwd)
-
-ifdef DEBUG
-  bindata_flags = -debug
-endif
-
-STATICCHECK_IGNORE =
-
-all: format staticcheck build test
-
-style:
-	@echo ">> checking code style"
-	@! $(GOFMT) -d $(shell find . -path ./vendor -prune -o -name '*.go' -print) | grep '^'
-
-check_license:
-	@echo ">> checking license header"
-	@./scripts/check_license.sh
-
-test: fixtures/.unpacked sysfs/fixtures/.unpacked
-	@echo ">> running all tests"
-	@$(GO) test -race $(shell $(GO) list ./... | grep -v /vendor/ | grep -v examples)
-
-format:
-	@echo ">> formatting code"
-	@$(GO) fmt $(pkgs)
-
-vet:
-	@echo ">> vetting code"
-	@$(GO) vet $(pkgs)
-
-staticcheck: $(STATICCHECK)
-	@echo ">> running staticcheck"
-	@$(STATICCHECK) -ignore "$(STATICCHECK_IGNORE)" $(pkgs)
+include Makefile.common
 
 %/.unpacked: %.ttar
+	@echo ">> extracting fixtures"
 	./ttar -C $(dir $*) -x -f $*.ttar
 	touch $@
 
-$(FIRST_GOPATH)/bin/staticcheck:
-	@GOOS= GOARCH= $(GO) get -u honnef.co/go/tools/cmd/staticcheck
+update_fixtures:
+	rm -vf fixtures/.unpacked
+	./ttar -c -f fixtures.ttar fixtures/
 
-.PHONY: all style check_license format test vet staticcheck
+.PHONY: build
+build:
 
-# Declaring the binaries at their default locations as PHONY targets is a hack
-# to ensure the latest version is downloaded on every make execution.
-# If this is not desired, copy/symlink these binaries to a different path and
-# set the respective environment variables.
-.PHONY: $(GOPATH)/bin/staticcheck
+.PHONY: test
+test: fixtures/.unpacked common-test
diff --git a/Makefile.common b/Makefile.common
new file mode 100644
index 0000000..c7f9ea6
--- /dev/null
+++ b/Makefile.common
@@ -0,0 +1,272 @@
+# Copyright 2018 The Prometheus Authors
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# A common Makefile that includes rules to be reused in different prometheus projects.
+# !!! Open PRs only against the prometheus/prometheus/Makefile.common repository!
+
+# Example usage :
+# Create the main Makefile in the root project directory.
+# include Makefile.common
+# customTarget:
+# 	@echo ">> Running customTarget"
+#
+
+# Ensure GOBIN is not set during build so that promu is installed to the correct path
+unexport GOBIN
+
+GO           ?= go
+GOFMT        ?= $(GO)fmt
+FIRST_GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH)))
+GOOPTS       ?=
+GOHOSTOS     ?= $(shell $(GO) env GOHOSTOS)
+GOHOSTARCH   ?= $(shell $(GO) env GOHOSTARCH)
+
+GO_VERSION        ?= $(shell $(GO) version)
+GO_VERSION_NUMBER ?= $(word 3, $(GO_VERSION))
+PRE_GO_111        ?= $(shell echo $(GO_VERSION_NUMBER) | grep -E 'go1\.(10|[0-9])\.')
+
+GOVENDOR :=
+GO111MODULE :=
+ifeq (, $(PRE_GO_111))
+	ifneq (,$(wildcard go.mod))
+		# Enforce Go modules support just in case the directory is inside GOPATH (and for Travis CI).
+		GO111MODULE := on
+
+		ifneq (,$(wildcard vendor))
+			# Always use the local vendor/ directory to satisfy the dependencies.
+			GOOPTS := $(GOOPTS) -mod=vendor
+		endif
+	endif
+else
+	ifneq (,$(wildcard go.mod))
+		ifneq (,$(wildcard vendor))
+$(warning This repository requires Go >= 1.11 because of Go modules)
+$(warning Some recipes may not work as expected as the current Go runtime is '$(GO_VERSION_NUMBER)')
+		endif
+	else
+		# This repository isn't using Go modules (yet).
+		GOVENDOR := $(FIRST_GOPATH)/bin/govendor
+	endif
+endif
+PROMU        := $(FIRST_GOPATH)/bin/promu
+pkgs          = ./...
+
+ifeq (arm, $(GOHOSTARCH))
+	GOHOSTARM ?= $(shell GOARM= $(GO) env GOARM)
+	GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH)v$(GOHOSTARM)
+else
+	GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH)
+endif
+
+PROMU_VERSION ?= 0.4.0
+PROMU_URL     := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz
+
+GOLANGCI_LINT :=
+GOLANGCI_LINT_OPTS ?=
+GOLANGCI_LINT_VERSION ?= v1.16.0
+# golangci-lint only supports linux, darwin and windows platforms on i386/amd64.
+# windows isn't included here because of the path separator being different.
+ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin))
+	ifeq ($(GOHOSTARCH),$(filter $(GOHOSTARCH),amd64 i386))
+		GOLANGCI_LINT := $(FIRST_GOPATH)/bin/golangci-lint
+	endif
+endif
+
+PREFIX                  ?= $(shell pwd)
+BIN_DIR                 ?= $(shell pwd)
+DOCKER_IMAGE_TAG        ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD))
+DOCKER_REPO             ?= prom
+
+DOCKER_ARCHS            ?= amd64
+
+BUILD_DOCKER_ARCHS = $(addprefix common-docker-,$(DOCKER_ARCHS))
+PUBLISH_DOCKER_ARCHS = $(addprefix common-docker-publish-,$(DOCKER_ARCHS))
+TAG_DOCKER_ARCHS = $(addprefix common-docker-tag-latest-,$(DOCKER_ARCHS))
+
+ifeq ($(GOHOSTARCH),amd64)
+        ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux freebsd darwin windows))
+                # Only supported on amd64
+                test-flags := -race
+        endif
+endif
+
+# This rule is used to forward a target like "build" to "common-build".  This
+# allows a new "build" target to be defined in a Makefile which includes this
+# one and override "common-build" without override warnings.
+%: common-% ;
+
+.PHONY: common-all
+common-all: precheck style check_license lint unused build test
+
+.PHONY: common-style
+common-style:
+	@echo ">> checking code style"
+	@fmtRes=$$($(GOFMT) -d $$(find . -path ./vendor -prune -o -name '*.go' -print)); \
+	if [ -n "$${fmtRes}" ]; then \
+		echo "gofmt checking failed!"; echo "$${fmtRes}"; echo; \
+		echo "Please ensure you are using $$($(GO) version) for formatting code."; \
+		exit 1; \
+	fi
+
+.PHONY: common-check_license
+common-check_license:
+	@echo ">> checking license header"
+	@licRes=$$(for file in $$(find . -type f -iname '*.go' ! -path './vendor/*') ; do \
+               awk 'NR<=3' $$file | grep -Eq "(Copyright|generated|GENERATED)" || echo $$file; \
+       done); \
+       if [ -n "$${licRes}" ]; then \
+               echo "license header checking failed:"; echo "$${licRes}"; \
+               exit 1; \
+       fi
+
+.PHONY: common-deps
+common-deps:
+	@echo ">> getting dependencies"
+ifdef GO111MODULE
+	GO111MODULE=$(GO111MODULE) $(GO) mod download
+else
+	$(GO) get $(GOOPTS) -t ./...
+endif
+
+.PHONY: common-test-short
+common-test-short:
+	@echo ">> running short tests"
+	GO111MODULE=$(GO111MODULE) $(GO) test -short $(GOOPTS) $(pkgs)
+
+.PHONY: common-test
+common-test:
+	@echo ">> running all tests"
+	GO111MODULE=$(GO111MODULE) $(GO) test $(test-flags) $(GOOPTS) $(pkgs)
+
+.PHONY: common-format
+common-format:
+	@echo ">> formatting code"
+	GO111MODULE=$(GO111MODULE) $(GO) fmt $(pkgs)
+
+.PHONY: common-vet
+common-vet:
+	@echo ">> vetting code"
+	GO111MODULE=$(GO111MODULE) $(GO) vet $(GOOPTS) $(pkgs)
+
+.PHONY: common-lint
+common-lint: $(GOLANGCI_LINT)
+ifdef GOLANGCI_LINT
+	@echo ">> running golangci-lint"
+ifdef GO111MODULE
+# 'go list' needs to be executed before staticcheck to prepopulate the modules cache.
+# Otherwise staticcheck might fail randomly for some reason not yet explained.
+	GO111MODULE=$(GO111MODULE) $(GO) list -e -compiled -test=true -export=false -deps=true -find=false -tags= -- ./... > /dev/null
+	GO111MODULE=$(GO111MODULE) $(GOLANGCI_LINT) run $(GOLANGCI_LINT_OPTS) $(pkgs)
+else
+	$(GOLANGCI_LINT) run $(pkgs)
+endif
+endif
+
+# For backward-compatibility.
+.PHONY: common-staticcheck
+common-staticcheck: lint
+
+.PHONY: common-unused
+common-unused: $(GOVENDOR)
+ifdef GOVENDOR
+	@echo ">> running check for unused packages"
+	@$(GOVENDOR) list +unused | grep . && exit 1 || echo 'No unused packages'
+else
+ifdef GO111MODULE
+	@echo ">> running check for unused/missing packages in go.mod"
+	GO111MODULE=$(GO111MODULE) $(GO) mod tidy
+ifeq (,$(wildcard vendor))
+	@git diff --exit-code -- go.sum go.mod
+else
+	@echo ">> running check for unused packages in vendor/"
+	GO111MODULE=$(GO111MODULE) $(GO) mod vendor
+	@git diff --exit-code -- go.sum go.mod vendor/
+endif
+endif
+endif
+
+.PHONY: common-build
+common-build: promu
+	@echo ">> building binaries"
+	GO111MODULE=$(GO111MODULE) $(PROMU) build --prefix $(PREFIX)
+
+.PHONY: common-tarball
+common-tarball: promu
+	@echo ">> building release tarball"
+	$(PROMU) tarball --prefix $(PREFIX) $(BIN_DIR)
+
+.PHONY: common-docker $(BUILD_DOCKER_ARCHS)
+common-docker: $(BUILD_DOCKER_ARCHS)
+$(BUILD_DOCKER_ARCHS): common-docker-%:
+	docker build -t "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" \
+		--build-arg ARCH="$*" \
+		--build-arg OS="linux" \
+		.
+
+.PHONY: common-docker-publish $(PUBLISH_DOCKER_ARCHS)
+common-docker-publish: $(PUBLISH_DOCKER_ARCHS)
+$(PUBLISH_DOCKER_ARCHS): common-docker-publish-%:
+	docker push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)"
+
+.PHONY: common-docker-tag-latest $(TAG_DOCKER_ARCHS)
+common-docker-tag-latest: $(TAG_DOCKER_ARCHS)
+$(TAG_DOCKER_ARCHS): common-docker-tag-latest-%:
+	docker tag "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:$(DOCKER_IMAGE_TAG)" "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$*:latest"
+
+.PHONY: common-docker-manifest
+common-docker-manifest:
+	DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create -a "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)" $(foreach ARCH,$(DOCKER_ARCHS),$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME)-linux-$(ARCH):$(DOCKER_IMAGE_TAG))
+	DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$(DOCKER_REPO)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG)"
+
+.PHONY: promu
+promu: $(PROMU)
+
+$(PROMU):
+	$(eval PROMU_TMP := $(shell mktemp -d))
+	curl -s -L $(PROMU_URL) | tar -xvzf - -C $(PROMU_TMP)
+	mkdir -p $(FIRST_GOPATH)/bin
+	cp $(PROMU_TMP)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM)/promu $(FIRST_GOPATH)/bin/promu
+	rm -r $(PROMU_TMP)
+
+.PHONY: proto
+proto:
+	@echo ">> generating code from proto files"
+	@./scripts/genproto.sh
+
+ifdef GOLANGCI_LINT
+$(GOLANGCI_LINT):
+	mkdir -p $(FIRST_GOPATH)/bin
+	curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(FIRST_GOPATH)/bin $(GOLANGCI_LINT_VERSION)
+endif
+
+ifdef GOVENDOR
+.PHONY: $(GOVENDOR)
+$(GOVENDOR):
+	GOOS= GOARCH= $(GO) get -u github.com/kardianos/govendor
+endif
+
+.PHONY: precheck
+precheck::
+
+define PRECHECK_COMMAND_template =
+precheck:: $(1)_precheck
+
+PRECHECK_COMMAND_$(1) ?= $(1) $$(strip $$(PRECHECK_OPTIONS_$(1)))
+.PHONY: $(1)_precheck
+$(1)_precheck:
+	@if ! $$(PRECHECK_COMMAND_$(1)) 1>/dev/null 2>&1; then \
+		echo "Execution of '$$(PRECHECK_COMMAND_$(1))' command failed. Is $(1) installed?"; \
+		exit 1; \
+	fi
+endef
diff --git a/bcache/get.go b/bcache/get.go
index b6d97de..8d404bd 100644
--- a/bcache/get.go
+++ b/bcache/get.go
@@ -22,8 +22,54 @@ import (
 	"path/filepath"
 	"strconv"
 	"strings"
+
+	"github.com/prometheus/procfs/internal/fs"
 )
 
+// FS represents the pseudo-filesystem proc, which provides an interface to
+// kernel data structures.
+type FS struct {
+	sys *fs.FS
+}
+
+// NewFS returns a new Bcache using the given sys fs mount point. It will error
+// if the mount point can't be read.
+func NewFS(mountPoint string) (FS, error) {
+	if strings.TrimSpace(mountPoint) == "" {
+		mountPoint = fs.DefaultSysMountPoint
+	}
+	fs, err := fs.NewFS(mountPoint)
+	if err != nil {
+		return FS{}, err
+	}
+	return FS{&fs}, nil
+}
+
+// Stats retrieves bcache runtime statistics for each bcache.
+func (fs FS) Stats() ([]*Stats, error) {
+	matches, err := filepath.Glob(fs.sys.Path("fs/bcache/*-*"))
+	if err != nil {
+		return nil, err
+	}
+
+	stats := make([]*Stats, 0, len(matches))
+	for _, uuidPath := range matches {
+		// "*-*" in glob above indicates the name of the bcache.
+		name := filepath.Base(uuidPath)
+
+		// stats
+		s, err := GetStats(uuidPath)
+		if err != nil {
+			return nil, err
+		}
+
+		s.Name = name
+		stats = append(stats, s)
+	}
+
+	return stats, nil
+}
+
 // ParsePseudoFloat parses the peculiar format produced by bcache's bch_hprint.
 func parsePseudoFloat(str string) (float64, error) {
 	ss := strings.Split(str, ".")
diff --git a/blockdevice/stats.go b/blockdevice/stats.go
new file mode 100644
index 0000000..efaeb81
--- /dev/null
+++ b/blockdevice/stats.go
@@ -0,0 +1,209 @@
+// Copyright 2018 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package blockdevice
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"strings"
+
+	"github.com/prometheus/procfs/internal/fs"
+)
+
+// Info contains identifying information for a block device such as a disk drive
+type Info struct {
+	MajorNumber uint32
+	MinorNumber uint32
+	DeviceName  string
+}
+
+// IOStats models the iostats data described in the kernel documentation
+// https://www.kernel.org/doc/Documentation/iostats.txt,
+// https://www.kernel.org/doc/Documentation/block/stat.txt,
+// and https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats
+type IOStats struct {
+	// ReadIOs is the number of reads completed successfully.
+	ReadIOs uint64
+	// ReadMerges is the number of reads merged.  Reads and writes
+	// which are adjacent to each other may be merged for efficiency.
+	ReadMerges uint64
+	// ReadSectors is the total number of sectors read successfully.
+	ReadSectors uint64
+	// ReadTicks is the total number of milliseconds spent by all reads.
+	ReadTicks uint64
+	// WriteIOs is the total number of writes completed successfully.
+	WriteIOs uint64
+	// WriteMerges is the number of reads merged.
+	WriteMerges uint64
+	// WriteSectors is the total number of sectors written successfully.
+	WriteSectors uint64
+	// WriteTicks is the total number of milliseconds spent by all writes.
+	WriteTicks uint64
+	// IOsInProgress is number of I/Os currently in progress.
+	IOsInProgress uint64
+	// IOsTotalTicks is the number of milliseconds spent doing I/Os.
+	// This field increases so long as IosInProgress is nonzero.
+	IOsTotalTicks uint64
+	// WeightedIOTicks is the weighted number of milliseconds spent doing I/Os.
+	// This can also be used to estimate average queue wait time for requests.
+	WeightedIOTicks uint64
+	// DiscardIOs is the total number of discards completed successfully.
+	DiscardIOs uint64
+	// DiscardMerges is the number of discards merged.
+	DiscardMerges uint64
+	// DiscardSectors is the total number of sectors discarded successfully.
+	DiscardSectors uint64
+	// DiscardTicks is the total number of milliseconds spent by all discards.
+	DiscardTicks uint64
+}
+
+// Diskstats combines the device Info and IOStats
+type Diskstats struct {
+	Info
+	IOStats
+	// IoStatsCount contains the number of io stats read.  For kernel versions
+	// 4.18+, there should be 18 fields read.  For earlier kernel versions this
+	// will be 14 because the discard values are not available.
+	IoStatsCount int
+}
+
+const (
+	procDiskstatsPath   = "diskstats"
+	procDiskstatsFormat = "%d %d %s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"
+	sysBlockPath        = "block"
+	sysBlockStatFormat  = "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d"
+)
+
+// FS represents the pseudo-filesystems proc and sys, which provides an
+// interface to kernel data structures.
+type FS struct {
+	proc *fs.FS
+	sys  *fs.FS
+}
+
+// NewFS returns a new XFS mounted under the given mountPoint. It will error
+// if the mount point can't be read.
+func NewFS(procMountPoint string, sysMountPoint string) (FS, error) {
+	if strings.TrimSpace(procMountPoint) == "" {
+		procMountPoint = fs.DefaultProcMountPoint
+	}
+	procfs, err := fs.NewFS(procMountPoint)
+	if err != nil {
+		return FS{}, err
+	}
+	if strings.TrimSpace(sysMountPoint) == "" {
+		sysMountPoint = fs.DefaultSysMountPoint
+	}
+	sysfs, err := fs.NewFS(sysMountPoint)
+	if err != nil {
+		return FS{}, err
+	}
+	return FS{&procfs, &sysfs}, nil
+}
+
+// ProcDiskstats reads the diskstats file and returns
+// an array of Diskstats (one per line/device)
+func (fs FS) ProcDiskstats() ([]Diskstats, error) {
+	file, err := os.Open(fs.proc.Path(procDiskstatsPath))
+	if err != nil {
+		return nil, err
+	}
+	defer file.Close()
+
+	diskstats := []Diskstats{}
+	scanner := bufio.NewScanner(file)
+	for scanner.Scan() {
+		d := &Diskstats{}
+		d.IoStatsCount, err = fmt.Sscanf(scanner.Text(), procDiskstatsFormat,
+			&d.MajorNumber,
+			&d.MinorNumber,
+			&d.DeviceName,
+			&d.ReadIOs,
+			&d.ReadMerges,
+			&d.ReadSectors,
+			&d.ReadTicks,
+			&d.WriteIOs,
+			&d.WriteMerges,
+			&d.WriteSectors,
+			&d.WriteTicks,
+			&d.IOsInProgress,
+			&d.IOsTotalTicks,
+			&d.WeightedIOTicks,
+			&d.DiscardIOs,
+			&d.DiscardMerges,
+			&d.DiscardSectors,
+			&d.DiscardTicks,
+		)
+		// The io.EOF error can be safely ignored because it just means we read fewer than
+		// the full 18 fields.
+		if err != nil && err != io.EOF {
+			return diskstats, err
+		}
+		if d.IoStatsCount == 14 || d.IoStatsCount == 18 {
+			diskstats = append(diskstats, *d)
+		}
+	}
+	return diskstats, scanner.Err()
+}
+
+// SysBlockDevices lists the device names from /sys/block/<dev>
+func (fs FS) SysBlockDevices() ([]string, error) {
+	deviceDirs, err := ioutil.ReadDir(fs.sys.Path(sysBlockPath))
+	if err != nil {
+		return nil, err
+	}
+	devices := []string{}
+	for _, deviceDir := range deviceDirs {
+		if deviceDir.IsDir() {
+			devices = append(devices, deviceDir.Name())
+		}
+	}
+	return devices, nil
+}
+
+// SysBlockDeviceStat returns stats for the block device read from /sys/block/<device>/stat.
+// The number of stats read will be 15 if the discard stats are available (kernel 4.18+)
+// and 11 if they are not available.
+func (fs FS) SysBlockDeviceStat(device string) (IOStats, int, error) {
+	stat := IOStats{}
+	bytes, err := ioutil.ReadFile(fs.sys.Path(sysBlockPath, device, "stat"))
+	if err != nil {
+		return stat, 0, err
+	}
+	count, err := fmt.Sscanf(strings.TrimSpace(string(bytes)), sysBlockStatFormat,
+		&stat.ReadIOs,
+		&stat.ReadMerges,
+		&stat.ReadSectors,
+		&stat.ReadTicks,
+		&stat.WriteIOs,
+		&stat.WriteMerges,
+		&stat.WriteSectors,
+		&stat.WriteTicks,
+		&stat.IOsInProgress,
+		&stat.IOsTotalTicks,
+		&stat.WeightedIOTicks,
+		&stat.DiscardIOs,
+		&stat.DiscardMerges,
+		&stat.DiscardSectors,
+		&stat.DiscardTicks,
+	)
+	// An io.EOF error is ignored because it just means we read fewer than the full 15 fields.
+	if err == io.EOF {
+		return stat, count, nil
+	}
+	return stat, count, err
+}
diff --git a/buddyinfo.go b/buddyinfo.go
index d3a8268..5cd22a8 100644
--- a/buddyinfo.go
+++ b/buddyinfo.go
@@ -43,7 +43,7 @@ func NewBuddyInfo() ([]BuddyInfo, error) {
 
 // NewBuddyInfo reads the buddyinfo statistics from the specified `proc` filesystem.
 func (fs FS) NewBuddyInfo() ([]BuddyInfo, error) {
-	file, err := os.Open(fs.Path("buddyinfo"))
+	file, err := os.Open(fs.proc.Path("buddyinfo"))
 	if err != nil {
 		return nil, err
 	}
diff --git a/fixtures.ttar b/fixtures.ttar
index 3ee8291..1242447 100644
--- a/fixtures.ttar
+++ b/fixtures.ttar
@@ -1,42 +1,48 @@
 # Archive created by ttar -c -f fixtures.ttar fixtures/
 Directory: fixtures
+Mode: 775
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/proc
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/26231
+Directory: fixtures/proc/26231
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26231/cmdline
+Path: fixtures/proc/26231/cmdline
 Lines: 1
 vimNULLBYTEtest.goNULLBYTE+10NULLBYTEEOF
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26231/comm
+Path: fixtures/proc/26231/comm
 Lines: 1
 vim
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26231/exe
+Path: fixtures/proc/26231/cwd
+SymlinkTo: /usr/bin
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/26231/exe
 SymlinkTo: /usr/bin/vim
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/26231/fd
+Directory: fixtures/proc/26231/fd
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26231/fd/0
+Path: fixtures/proc/26231/fd/0
 SymlinkTo: ../../symlinktargets/abc
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26231/fd/1
+Path: fixtures/proc/26231/fd/1
 SymlinkTo: ../../symlinktargets/def
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26231/fd/10
+Path: fixtures/proc/26231/fd/10
 SymlinkTo: ../../symlinktargets/xyz
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26231/fd/2
+Path: fixtures/proc/26231/fd/2
 SymlinkTo: ../../symlinktargets/ghi
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26231/fd/3
+Path: fixtures/proc/26231/fd/3
 SymlinkTo: ../../symlinktargets/uvw
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26231/io
+Path: fixtures/proc/26231/io
 Lines: 7
 rchar: 750339
 wchar: 818609
@@ -47,7 +53,7 @@ write_bytes: 2048
 cancelled_write_bytes: -1024
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26231/limits
+Path: fixtures/proc/26231/limits
 Lines: 17
 Limit                     Soft Limit           Hard Limit           Units
 Max cpu time              unlimited            unlimited            seconds
@@ -68,7 +74,7 @@ Max realtime priority     0                    0
 Max realtime timeout      unlimited            unlimited            us
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26231/mountstats
+Path: fixtures/proc/26231/mountstats
 Lines: 19
 device rootfs mounted on / with fstype rootfs
 device sysfs mounted on /sys with fstype sysfs
@@ -91,10 +97,10 @@ device 192.168.1.1:/srv/test mounted on /mnt/nfs/test with fstype nfs4 statvers=
 
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/26231/net
+Directory: fixtures/proc/26231/net
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26231/net/dev
+Path: fixtures/proc/26231/net/dev
 Lines: 4
 Inter-|   Receive                                                |  Transmit
  face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
@@ -102,133 +108,226 @@ Inter-|   Receive                                                |  Transmit
   eth0:     438       5    0    0    0     0          0         0      648       8    0    0    0     0       0          0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/26231/ns
+Directory: fixtures/proc/26231/ns
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26231/ns/mnt
+Path: fixtures/proc/26231/ns/mnt
 SymlinkTo: mnt:[4026531840]
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26231/ns/net
+Path: fixtures/proc/26231/ns/net
 SymlinkTo: net:[4026531993]
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26231/stat
+Path: fixtures/proc/26231/root
+SymlinkTo: /
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/26231/status
+Lines: 53
+
+Name:	prometheus
+Umask:	0022
+State:	S (sleeping)
+Tgid:	1
+Ngid:	0
+Pid:	1
+PPid:	0
+TracerPid:	0
+Uid:	0	0	0	0
+Gid:	0	0	0	0
+FDSize:	128
+Groups:
+NStgid:	1
+NSpid:	1
+NSpgid:	1
+NSsid:	1
+VmPeak:	   58472 kB
+VmSize:	   58440 kB
+VmLck:	       0 kB
+VmPin:	       0 kB
+VmHWM:	    8028 kB
+VmRSS:	    6716 kB
+RssAnon:	    2092 kB
+RssFile:	    4624 kB
+RssShmem:	       0 kB
+VmData:	    2580 kB
+VmStk:	     136 kB
+VmExe:	     948 kB
+VmLib:	    6816 kB
+VmPTE:	     128 kB
+VmPMD:	      12 kB
+VmSwap:	     660 kB
+HugetlbPages:	       0 kB
+Threads:	1
+SigQ:	8/63965
+SigPnd:	0000000000000000
+ShdPnd:	0000000000000000
+SigBlk:	7be3c0fe28014a03
+SigIgn:	0000000000001000
+SigCgt:	00000001800004ec
+CapInh:	0000000000000000
+CapPrm:	0000003fffffffff
+CapEff:	0000003fffffffff
+CapBnd:	0000003fffffffff
+CapAmb:	0000000000000000
+Seccomp:	0
+Cpus_allowed:	ff
+Cpus_allowed_list:	0-7
+Mems_allowed:	00000000,00000001
+Mems_allowed_list:	0
+voluntary_ctxt_switches:	4742839
+nonvoluntary_ctxt_switches:	1727500
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/26231/stat
 Lines: 1
 26231 (vim) R 5392 7446 5392 34835 7446 4218880 32533 309516 26 82 1677 44 158 99 20 0 1 0 82375 56274944 1981 18446744073709551615 4194304 6294284 140736914091744 140736914087944 139965136429984 0 0 12288 1870679807 0 0 0 17 0 0 0 31 0 0 8391624 8481048 16420864 140736914093252 140736914093279 140736914093279 140736914096107 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/26232
+Directory: fixtures/proc/26232
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26232/cmdline
+Path: fixtures/proc/26232/cmdline
 Lines: 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26232/comm
+Path: fixtures/proc/26232/comm
 Lines: 1
 ata_sff
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/26232/fd
+Path: fixtures/proc/26232/cwd
+SymlinkTo: /does/not/exist
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/proc/26232/fd
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26232/fd/0
+Path: fixtures/proc/26232/fd/0
 SymlinkTo: ../../symlinktargets/abc
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26232/fd/1
+Path: fixtures/proc/26232/fd/1
 SymlinkTo: ../../symlinktargets/def
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26232/fd/2
+Path: fixtures/proc/26232/fd/2
 SymlinkTo: ../../symlinktargets/ghi
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26232/fd/3
+Path: fixtures/proc/26232/fd/3
 SymlinkTo: ../../symlinktargets/uvw
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26232/fd/4
+Path: fixtures/proc/26232/fd/4
 SymlinkTo: ../../symlinktargets/xyz
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26232/limits
+Path: fixtures/proc/26232/limits
 Lines: 17
-Limit                     Soft Limit           Hard Limit           Units     
-Max cpu time              unlimited            unlimited            seconds   
-Max file size             unlimited            unlimited            bytes     
-Max data size             unlimited            unlimited            bytes     
-Max stack size            8388608              unlimited            bytes     
-Max core file size        0                    unlimited            bytes     
-Max resident set          unlimited            unlimited            bytes     
-Max processes             29436                29436                processes 
-Max open files            1024                 4096                 files     
-Max locked memory         65536                65536                bytes     
-Max address space         unlimited            unlimited            bytes     
-Max file locks            unlimited            unlimited            locks     
-Max pending signals       29436                29436                signals   
-Max msgqueue size         819200               819200               bytes     
-Max nice priority         0                    0                    
-Max realtime priority     0                    0                    
-Max realtime timeout      unlimited            unlimited            us        
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26232/stat
+Limit                     Soft Limit           Hard Limit           Units
+Max cpu time              unlimited            unlimited            seconds
+Max file size             unlimited            unlimited            bytes
+Max data size             unlimited            unlimited            bytes
+Max stack size            8388608              unlimited            bytes
+Max core file size        0                    unlimited            bytes
+Max resident set          unlimited            unlimited            bytes
+Max processes             29436                29436                processes
+Max open files            1024                 4096                 files
+Max locked memory         65536                65536                bytes
+Max address space         unlimited            unlimited            bytes
+Max file locks            unlimited            unlimited            locks
+Max pending signals       29436                29436                signals
+Max msgqueue size         819200               819200               bytes
+Max nice priority         0                    0
+Max realtime priority     0                    0
+Max realtime timeout      unlimited            unlimited            us
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/26232/root
+SymlinkTo: /does/not/exist
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/26232/stat
 Lines: 1
 33 (ata_sff) S 2 0 0 0 -1 69238880 0 0 0 0 0 0 0 0 0 -20 1 0 5 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744073709551615 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/26233
+Directory: fixtures/proc/26233
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/26233/cmdline
+Path: fixtures/proc/26233/cmdline
 Lines: 1
 com.github.uiautomatorNULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTENULLBYTEEOF
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/584
+Directory: fixtures/proc/584
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/584/stat
+Path: fixtures/proc/584/stat
 Lines: 2
 1020 ((a b ) ( c d) ) R 28378 1020 28378 34842 1020 4218880 286 0 0 0 0 0 0 0 20 0 1 0 10839175 10395648 155 18446744073709551615 4194304 4238788 140736466511168 140736466511168 140609271124624 0 0 0 0 0 0 0 17 5 0 0 0 0 0 6336016 6337300 25579520 140736466515030 140736466515061 140736466515061 140736466518002 0
 #!/bin/cat /proc/self/stat
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/buddyinfo
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/buddyinfo/short
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/buddyinfo/short/buddyinfo
-Lines: 3
-Node 0, zone
-Node 0, zone
-Node 0, zone
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/buddyinfo/sizemismatch
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/buddyinfo/sizemismatch/buddyinfo
+Path: fixtures/proc/buddyinfo
 Lines: 3
-Node 0, zone      DMA      1      0      1      0      2      1      1      0      1      1      3 
-Node 0, zone    DMA32    759    572    791    475    194     45     12      0      0      0      0      0
-Node 0, zone   Normal   4381   1093    185   1530    567    102      4      0      0      0
+Node 0, zone      DMA      1      0      1      0      2      1      1      0      1      1      3
+Node 0, zone    DMA32    759    572    791    475    194     45     12      0      0      0      0
+Node 0, zone   Normal   4381   1093    185   1530    567    102      4      0      0      0      0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/buddyinfo/valid
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/buddyinfo/valid/buddyinfo
-Lines: 3
-Node 0, zone      DMA      1      0      1      0      2      1      1      0      1      1      3 
-Node 0, zone    DMA32    759    572    791    475    194     45     12      0      0      0      0 
-Node 0, zone   Normal   4381   1093    185   1530    567    102      4      0      0      0      0 
-Mode: 644
+Path: fixtures/proc/diskstats
+Lines: 49
+   1       0 ram0 0 0 0 0 0 0 0 0 0 0 0
+   1       1 ram1 0 0 0 0 0 0 0 0 0 0 0
+   1       2 ram2 0 0 0 0 0 0 0 0 0 0 0
+   1       3 ram3 0 0 0 0 0 0 0 0 0 0 0
+   1       4 ram4 0 0 0 0 0 0 0 0 0 0 0
+   1       5 ram5 0 0 0 0 0 0 0 0 0 0 0
+   1       6 ram6 0 0 0 0 0 0 0 0 0 0 0
+   1       7 ram7 0 0 0 0 0 0 0 0 0 0 0
+   1       8 ram8 0 0 0 0 0 0 0 0 0 0 0
+   1       9 ram9 0 0 0 0 0 0 0 0 0 0 0
+   1      10 ram10 0 0 0 0 0 0 0 0 0 0 0
+   1      11 ram11 0 0 0 0 0 0 0 0 0 0 0
+   1      12 ram12 0 0 0 0 0 0 0 0 0 0 0
+   1      13 ram13 0 0 0 0 0 0 0 0 0 0 0
+   1      14 ram14 0 0 0 0 0 0 0 0 0 0 0
+   1      15 ram15 0 0 0 0 0 0 0 0 0 0 0
+   7       0 loop0 0 0 0 0 0 0 0 0 0 0 0
+   7       1 loop1 0 0 0 0 0 0 0 0 0 0 0
+   7       2 loop2 0 0 0 0 0 0 0 0 0 0 0
+   7       3 loop3 0 0 0 0 0 0 0 0 0 0 0
+   7       4 loop4 0 0 0 0 0 0 0 0 0 0 0
+   7       5 loop5 0 0 0 0 0 0 0 0 0 0 0
+   7       6 loop6 0 0 0 0 0 0 0 0 0 0 0
+   7       7 loop7 0 0 0 0 0 0 0 0 0 0 0
+   8       0 sda 25354637 34367663 1003346126 18492372 28444756 11134226 505697032 63877960 0 9653880 82621804
+   8       1 sda1 250 0 2000 36 0 0 0 0 0 36 36
+   8       2 sda2 246 0 1968 32 0 0 0 0 0 32 32
+   8       3 sda3 340 13 2818 52 11 8 152 8 0 56 60
+   8       4 sda4 25353629 34367650 1003337964 18492232 27448755 11134218 505696880 61593380 0 7576432 80332428
+ 252       0 dm-0 59910002 0 1003337218 46229572 39231014 0 505696880 1158557800 0 11325968 1206301256
+ 252       1 dm-1 388 0 3104 84 74 0 592 0 0 76 84
+ 252       2 dm-2 11571 0 308350 6536 153522 0 5093416 122884 0 65400 129416
+ 252       3 dm-3 3870 0 3870 104 0 0 0 0 0 16 104
+ 252       4 dm-4 392 0 1034 28 38 0 137 16 0 24 44
+ 252       5 dm-5 3729 0 84279 924 98918 0 1151688 104684 0 58848 105632
+ 179       0 mmcblk0 192 3 1560 156 0 0 0 0 0 136 156
+ 179       1 mmcblk0p1 17 3 160 24 0 0 0 0 0 24 24
+ 179       2 mmcblk0p2 95 0 760 68 0 0 0 0 0 68 68
+   2       0 fd0 2 0 16 80 0 0 0 0 0 80 80
+ 254       0 vda 1775784 15386 32670882 8655768 6038856 20711856 213637440 2069221364 0 41614592 2077872228
+ 254       1 vda1 668 85 5984 956 207 4266 35784 32772 0 8808 33720
+ 254       2 vda2 1774936 15266 32663262 8654692 5991028 20707590 213601656 2069152216 0 41607628 2077801992
+  11       0 sr0 0 0 0 0 0 0 0 0 0 0 0
+ 259       0 nvme0n1 47114 4 4643973 21650 1078320 43950 39451633 1011053 0 222766 1032546
+ 259       1 nvme0n1p1 1140 0 9370 16 1 0 1 0 0 16 16
+ 259       2 nvme0n1p2 45914 4 4631243 21626 1036885 43950 39451632 919480 0 131580 940970
+   8       0 sdb 326552 841 9657779 84 41822 2895 1972905 5007 0 60730 67070 68851 0 1925173784 11130
+   8       1 sdb1 231 3 34466 4 24 23 106 0 0 64 64 0 0 0 0
+   8       2 sdb2 326310 838 9622281 67 40726 2872 1972799 4924 0 58250 64567 68851 0 1925173784 11130
+Mode: 664
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs
+Directory: fixtures/proc/fs
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/xfs
+Directory: fixtures/proc/fs/xfs
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/xfs/stat
+Path: fixtures/proc/fs/xfs/stat
 Lines: 23
 extent_alloc 92447 97589 92448 93751
 abt 0 0 0 0
@@ -255,18 +354,18 @@ xpc 399724544 92823103 86219234
 debug 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/mdstat
+Path: fixtures/proc/mdstat
 Lines: 26
 Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10]
 md3 : active raid6 sda1[8] sdh1[7] sdg1[6] sdf1[5] sde1[11] sdd1[3] sdc1[10] sdb1[9]
       5853468288 blocks super 1.2 level 6, 64k chunk, algorithm 2 [8/8] [UUUUUUUU]
-      
+
 md127 : active raid1 sdi2[0] sdj2[1]
       312319552 blocks [2/2] [UU]
-      
+
 md0 : active raid1 sdk[2](S) sdi1[0] sdj1[1]
       248896 blocks [2/2] [UU]
-      
+
 md4 : inactive raid1 sda3[0] sdb3[1]
       4883648 blocks [2/2] [UU]
 
@@ -285,10 +384,10 @@ md7 : active raid6 sdb1[0] sde1[3] sdd1[2] sdc1[1]
 unused devices: <none>
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/net
+Directory: fixtures/proc/net
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/net/dev
+Path: fixtures/proc/net/dev
 Lines: 6
 Inter-|   Receive                                                |  Transmit
  face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
@@ -298,7 +397,7 @@ docker0:    2568      38    0    0    0     0          0         0      438
   eth0: 874354587 1036395    0    0    0     0          0         0 563352563  732147    0    0    0     0       0          0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/net/ip_vs
+Path: fixtures/proc/net/ip_vs
 Lines: 21
 IP Virtual Server version 1.2.1 (size=4096)
 Prot LocalAddress:Port Scheduler Flags
@@ -323,7 +422,7 @@ FWM  10001000 wlc
   -> C0A83215:0CEA      Route   0      0          2
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/net/ip_vs_stats
+Path: fixtures/proc/net/ip_vs_stats
 Lines: 6
    Total Incoming Outgoing         Incoming         Outgoing
    Conns  Packets  Packets            Bytes            Bytes
@@ -333,10 +432,10 @@ Lines: 6
        4    1FB3C        0          1282A8F                0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/net/rpc
+Directory: fixtures/proc/net/rpc
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/net/rpc/nfs
+Path: fixtures/proc/net/rpc/nfs
 Lines: 5
 net 18628 0 18628 6
 rpc 4329785 0 4338291
@@ -345,7 +444,7 @@ proc3 22 1 4084749 29200 94754 32580 186 47747 7981 8639 0 6356 0 6962 0 7958 0
 proc4 61 1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/net/rpc/nfsd
+Path: fixtures/proc/net/rpc/nfsd
 Lines: 11
 rc 0 6 18622
 fh 0 0 0 0 0
@@ -360,7 +459,7 @@ proc4 2 2 10853
 proc4ops 72 0 0 0 1098 2 0 0 0 0 8179 5896 0 0 0 0 5900 0 0 2 0 2 0 9609 0 2 150 1272 0 0 0 1236 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/net/xfrm_stat
+Path: fixtures/proc/net/xfrm_stat
 Lines: 28
 XfrmInError                     1
 XfrmInBufferError               2
@@ -392,10 +491,30 @@ XfrmOutStateInvalid             28765
 XfrmAcquireError                24532
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/self
+Directory: fixtures/proc/pressure
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/pressure/cpu
+Lines: 1
+some avg10=0.10 avg60=2.00 avg300=3.85 total=15
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/pressure/io
+Lines: 2
+some avg10=0.10 avg60=2.00 avg300=3.85 total=15
+full avg10=0.20 avg60=3.00 avg300=4.95 total=25
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/pressure/memory
+Lines: 2
+some avg10=0.10 avg60=2.00 avg300=3.85 total=15
+full avg10=0.20 avg60=3.00 avg300=4.95 total=25
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/self
 SymlinkTo: 26231
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/stat
+Path: fixtures/proc/stat
 Lines: 16
 cpu  301854 612 111922 8979004 3552 2 3944 0 0 0
 cpu0 44490 19 21045 1087069 220 1 3410 0 0 0
@@ -415,32 +534,1238 @@ procs_blocked 1
 softirq 5057579 250191 1481983 1647 211099 186066 0 1783454 622196 12499 508444
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/symlinktargets
+Directory: fixtures/proc/symlinktargets
 Mode: 755
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/symlinktargets/README
+Path: fixtures/proc/symlinktargets/README
 Lines: 2
 This directory contains some empty files that are the symlinks the files in the "fd" directory point to.
 They are otherwise ignored by the tests
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/symlinktargets/abc
+Path: fixtures/proc/symlinktargets/abc
+Lines: 0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/symlinktargets/def
+Lines: 0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/symlinktargets/ghi
 Lines: 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/symlinktargets/def
+Path: fixtures/proc/symlinktargets/uvw
+Lines: 0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/proc/symlinktargets/xyz
+Lines: 0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/block
+Mode: 775
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/block/dm-0
+Mode: 775
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/block/dm-0/stat
+Lines: 1
+6447303        0 710266738  1529043   953216        0 31201176  4557464        0   796160  6088971
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/block/sda
+Mode: 775
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/block/sda/stat
+Lines: 1
+9652963   396792 759304206   412943  8422549  6731723 286915323 13947418        0  5658367 19174573 1 2 3 12
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/class
+Mode: 775
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/class/net
+Mode: 775
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/class/net/eth0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/addr_assign_type
+Lines: 1
+3
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/addr_len
+Lines: 1
+6
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/address
+Lines: 1
+01:01:01:01:01:01
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/broadcast
+Lines: 1
+ff:ff:ff:ff:ff:ff
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/carrier
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/carrier_changes
+Lines: 1
+2
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/carrier_down_count
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/carrier_up_count
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/dev_id
+Lines: 1
+0x20
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/dormant
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/duplex
+Lines: 1
+full
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/flags
+Lines: 1
+0x1303
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/ifalias
 Lines: 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/symlinktargets/ghi
+Path: fixtures/sys/class/net/eth0/ifindex
+Lines: 1
+2
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/iflink
+Lines: 1
+2
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/link_mode
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/mtu
+Lines: 1
+1500
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/name_assign_type
+Lines: 1
+2
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/netdev_group
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/operstate
+Lines: 1
+up
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/phys_port_id
 Lines: 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/symlinktargets/uvw
+Path: fixtures/sys/class/net/eth0/phys_port_name
 Lines: 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/symlinktargets/xyz
+Path: fixtures/sys/class/net/eth0/phys_switch_id
 Lines: 0
 Mode: 644
 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/speed
+Lines: 1
+1000
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/tx_queue_len
+Lines: 1
+1000
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/net/eth0/type
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/class/power_supply
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/class/power_supply/AC
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/AC/online
+Lines: 1
+0
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/AC/type
+Lines: 1
+Mains
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/AC/uevent
+Lines: 2
+POWER_SUPPLY_NAME=AC
+POWER_SUPPLY_ONLINE=0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/class/power_supply/BAT0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/alarm
+Lines: 1
+2503000
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/capacity
+Lines: 1
+98
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/capacity_level
+Lines: 1
+Normal
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/charge_start_threshold
+Lines: 1
+95
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/charge_stop_threshold
+Lines: 1
+100
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/cycle_count
+Lines: 1
+0
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/energy_full
+Lines: 1
+50060000
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/energy_full_design
+Lines: 1
+47520000
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/energy_now
+Lines: 1
+49450000
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/manufacturer
+Lines: 1
+LGC
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/model_name
+Lines: 1
+LNV-45N1
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/power_now
+Lines: 1
+4830000
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/present
+Lines: 1
+1
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/serial_number
+Lines: 1
+38109
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/status
+Lines: 1
+Discharging
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/technology
+Lines: 1
+Li-ion
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/type
+Lines: 1
+Battery
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/uevent
+Lines: 16
+POWER_SUPPLY_NAME=BAT0
+POWER_SUPPLY_STATUS=Discharging
+POWER_SUPPLY_PRESENT=1
+POWER_SUPPLY_TECHNOLOGY=Li-ion
+POWER_SUPPLY_CYCLE_COUNT=0
+POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
+POWER_SUPPLY_VOLTAGE_NOW=12229000
+POWER_SUPPLY_POWER_NOW=4830000
+POWER_SUPPLY_ENERGY_FULL_DESIGN=47520000
+POWER_SUPPLY_ENERGY_FULL=50060000
+POWER_SUPPLY_ENERGY_NOW=49450000
+POWER_SUPPLY_CAPACITY=98
+POWER_SUPPLY_CAPACITY_LEVEL=Normal
+POWER_SUPPLY_MODEL_NAME=LNV-45N1
+POWER_SUPPLY_MANUFACTURER=LGC
+POWER_SUPPLY_SERIAL_NUMBER=38109
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/voltage_min_design
+Lines: 1
+10800000
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/power_supply/BAT0/voltage_now
+Lines: 1
+12229000
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/class/thermal
+Mode: 775
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/class/thermal/thermal_zone0
+Mode: 775
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/thermal/thermal_zone0/policy
+Lines: 1
+step_wise
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/thermal/thermal_zone0/temp
+Lines: 1
+49925
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/thermal/thermal_zone0/type
+Lines: 1
+bcm2835_thermal
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/class/thermal/thermal_zone1
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/thermal/thermal_zone1/mode
+Lines: 1
+enabled
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/thermal/thermal_zone1/passive
+Lines: 1
+0
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/thermal/thermal_zone1/policy
+Lines: 1
+step_wise
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/thermal/thermal_zone1/temp
+Lines: 1
+44000
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/class/thermal/thermal_zone1/type
+Lines: 1
+acpitz
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/dirty_data
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/bypassed
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/cache_bypass_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/cache_bypass_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/cache_hit_ratio
+Lines: 1
+100
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/cache_hits
+Lines: 1
+289
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/cache_miss_collisions
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/cache_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/cache_readaheads
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/bypassed
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/cache_bypass_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/cache_bypass_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/cache_hit_ratio
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/cache_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/cache_miss_collisions
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/cache_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/cache_readaheads
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/bypassed
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/cache_bypass_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/cache_bypass_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/cache_hit_ratio
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/cache_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/cache_miss_collisions
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/cache_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/cache_readaheads
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/bypassed
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/cache_bypass_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/cache_bypass_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/cache_hit_ratio
+Lines: 1
+100
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/cache_hits
+Lines: 1
+546
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/cache_miss_collisions
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/cache_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/cache_readaheads
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata5
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata5/host4
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0/block
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0/block/sdc
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0/block/sdc/bcache
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0/block/sdc/bcache/io_errors
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0/block/sdc/bcache/metadata_written
+Lines: 1
+512
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0/block/sdc/bcache/priority_stats
+Lines: 5
+Unused:		99%
+Metadata:	0%
+Average:	10473
+Sectors per Q:	64
+Quantiles:	[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946]
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0/block/sdc/bcache/written
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/system
+Mode: 775
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/system/cpu
+Mode: 775
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/system/cpu/cpu0
+Mode: 775
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu0/cpufreq
+SymlinkTo: ../cpufreq/policy0
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/system/cpu/cpu1
+Mode: 775
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/system/cpu/cpu1/cpufreq
+Mode: 775
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/cpufreq/cpuinfo_cur_freq
+Lines: 1
+1200195
+Mode: 400
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/cpufreq/cpuinfo_max_freq
+Lines: 1
+3300000
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/cpufreq/cpuinfo_min_freq
+Lines: 1
+1200000
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/cpufreq/cpuinfo_transition_latency
+Lines: 1
+4294967295
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/cpufreq/related_cpus
+Lines: 1
+1
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/cpufreq/scaling_available_governors
+Lines: 1
+performance powersave
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/cpufreq/scaling_driver
+Lines: 1
+intel_pstate
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/cpufreq/scaling_governor
+Lines: 1
+powersave
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/cpufreq/scaling_max_freq
+Lines: 1
+3300000
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/cpufreq/scaling_min_freq
+Lines: 1
+1200000
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpu1/cpufreq/scaling_setspeed
+Lines: 1
+<unsupported>
+Mode: 664
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/system/cpu/cpufreq
+Mode: 775
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/system/cpu/cpufreq/policy0
+Mode: 775
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpufreq/policy0/affected_cpus
+Lines: 1
+0
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpufreq/policy0/cpuinfo_max_freq
+Lines: 1
+2400000
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpufreq/policy0/cpuinfo_min_freq
+Lines: 1
+800000
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpufreq/policy0/cpuinfo_transition_latency
+Lines: 1
+0
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpufreq/policy0/related_cpus
+Lines: 1
+0
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpufreq/policy0/scaling_available_governors
+Lines: 1
+performance powersave
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq
+Lines: 1
+1219917
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpufreq/policy0/scaling_driver
+Lines: 1
+intel_pstate
+Mode: 444
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpufreq/policy0/scaling_governor
+Lines: 1
+powersave
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq
+Lines: 1
+2400000
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpufreq/policy0/scaling_min_freq
+Lines: 1
+800000
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/devices/system/cpu/cpufreq/policy0/scaling_setspeed
+Lines: 1
+<unsupported>
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/devices/system/cpu/cpufreq/policy1
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/bcache
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/average_key_size
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0
+Mode: 777
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/dirty_data
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/bypassed
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/cache_bypass_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/cache_bypass_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/cache_hit_ratio
+Lines: 1
+100
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/cache_hits
+Lines: 1
+289
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/cache_miss_collisions
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/cache_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/cache_readaheads
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/bypassed
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/cache_bypass_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/cache_bypass_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/cache_hit_ratio
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/cache_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/cache_miss_collisions
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/cache_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/cache_readaheads
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/bypassed
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/cache_bypass_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/cache_bypass_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/cache_hit_ratio
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/cache_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/cache_miss_collisions
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/cache_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/cache_readaheads
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/bypassed
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/cache_bypass_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/cache_bypass_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/cache_hit_ratio
+Lines: 1
+100
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/cache_hits
+Lines: 1
+546
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/cache_miss_collisions
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/cache_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/cache_readaheads
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/btree_cache_size
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/cache0
+Mode: 777
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/cache0/io_errors
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/cache0/metadata_written
+Lines: 1
+512
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/cache0/priority_stats
+Lines: 5
+Unused:		99%
+Metadata:	0%
+Average:	10473
+Sectors per Q:	64
+Quantiles:	[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946]
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/cache0/written
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/cache_available_percent
+Lines: 1
+100
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/congested
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/internal
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/internal/active_journal_entries
+Lines: 1
+1
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/internal/btree_nodes
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/internal/btree_read_average_duration_us
+Lines: 1
+1305
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/internal/cache_read_races
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/root_usage_percent
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/bypassed
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/cache_bypass_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/cache_bypass_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/cache_hit_ratio
+Lines: 1
+100
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/cache_hits
+Lines: 1
+289
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/cache_miss_collisions
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/cache_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/cache_readaheads
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/bypassed
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/cache_bypass_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/cache_bypass_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/cache_hit_ratio
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/cache_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/cache_miss_collisions
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/cache_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/cache_readaheads
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/bypassed
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/cache_bypass_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/cache_bypass_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/cache_hit_ratio
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/cache_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/cache_miss_collisions
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/cache_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/cache_readaheads
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/bypassed
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/cache_bypass_hits
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/cache_bypass_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/cache_hit_ratio
+Lines: 1
+100
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/cache_hits
+Lines: 1
+546
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/cache_miss_collisions
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/cache_misses
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/cache_readaheads
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/tree_depth
+Lines: 1
+0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/xfs
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/xfs/sda1
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/xfs/sda1/stats
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/xfs/sda1/stats/stats
+Lines: 1
+extent_alloc 1 0 0 0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/xfs/sdb1
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Directory: fixtures/sys/fs/xfs/sdb1/stats
+Mode: 755
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+Path: fixtures/sys/fs/xfs/sdb1/stats/stats
+Lines: 1
+extent_alloc 2 0 0 0
+Mode: 644
+# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/fs.go b/fs.go
index b6c6b2c..9c56c83 100644
--- a/fs.go
+++ b/fs.go
@@ -14,69 +14,24 @@
 package procfs
 
 import (
-	"fmt"
-	"os"
-	"path"
-
-	"github.com/prometheus/procfs/nfs"
-	"github.com/prometheus/procfs/xfs"
+	"github.com/prometheus/procfs/internal/fs"
 )
 
-// FS represents the pseudo-filesystem proc, which provides an interface to
+// FS represents the pseudo-filesystem sys, which provides an interface to
 // kernel data structures.
-type FS string
+type FS struct {
+	proc fs.FS
+}
 
 // DefaultMountPoint is the common mount point of the proc filesystem.
-const DefaultMountPoint = "/proc"
+const DefaultMountPoint = fs.DefaultProcMountPoint
 
-// NewFS returns a new FS mounted under the given mountPoint. It will error
-// if the mount point can't be read.
+// NewFS returns a new proc FS mounted under the given proc mountPoint. It will error
+// if the mount point dirctory can't be read or is a file.
 func NewFS(mountPoint string) (FS, error) {
-	info, err := os.Stat(mountPoint)
+	fs, err := fs.NewFS(mountPoint)
 	if err != nil {
-		return "", fmt.Errorf("could not read %s: %s", mountPoint, err)
-	}
-	if !info.IsDir() {
-		return "", fmt.Errorf("mount point %s is not a directory", mountPoint)
+		return FS{}, err
 	}
-
-	return FS(mountPoint), nil
-}
-
-// Path returns the path of the given subsystem relative to the procfs root.
-func (fs FS) Path(p ...string) string {
-	return path.Join(append([]string{string(fs)}, p...)...)
-}
-
-// XFSStats retrieves XFS filesystem runtime statistics.
-func (fs FS) XFSStats() (*xfs.Stats, error) {
-	f, err := os.Open(fs.Path("fs/xfs/stat"))
-	if err != nil {
-		return nil, err
-	}
-	defer f.Close()
-
-	return xfs.ParseStats(f)
-}
-
-// NFSClientRPCStats retrieves NFS client RPC statistics.
-func (fs FS) NFSClientRPCStats() (*nfs.ClientRPCStats, error) {
-	f, err := os.Open(fs.Path("net/rpc/nfs"))
-	if err != nil {
-		return nil, err
-	}
-	defer f.Close()
-
-	return nfs.ParseClientRPCStats(f)
-}
-
-// NFSdServerRPCStats retrieves NFS daemon RPC statistics.
-func (fs FS) NFSdServerRPCStats() (*nfs.ServerRPCStats, error) {
-	f, err := os.Open(fs.Path("net/rpc/nfsd"))
-	if err != nil {
-		return nil, err
-	}
-	defer f.Close()
-
-	return nfs.ParseServerRPCStats(f)
+	return FS{fs}, nil
 }
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..8a1b839
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module github.com/prometheus/procfs
+
+require golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..7827dd3
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,2 @@
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
diff --git a/internal/fs/fs.go b/internal/fs/fs.go
new file mode 100644
index 0000000..c66a1cf
--- /dev/null
+++ b/internal/fs/fs.go
@@ -0,0 +1,52 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fs
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+)
+
+const (
+	// DefaultProcMountPoint is the common mount point of the proc filesystem.
+	DefaultProcMountPoint = "/proc"
+
+	// DefaultSysMountPoint is the common mount point of the sys filesystem.
+	DefaultSysMountPoint = "/sys"
+)
+
+// FS represents a pseudo-filesystem, normally /proc or /sys, which provides an
+// interface to kernel data structures.
+type FS string
+
+// NewFS returns a new FS mounted under the given mountPoint. It will error
+// if the mount point can't be read.
+func NewFS(mountPoint string) (FS, error) {
+	info, err := os.Stat(mountPoint)
+	if err != nil {
+		return "", fmt.Errorf("could not read %s: %s", mountPoint, err)
+	}
+	if !info.IsDir() {
+		return "", fmt.Errorf("mount point %s is not a directory", mountPoint)
+	}
+
+	return FS(mountPoint), nil
+}
+
+// Path appends the given path elements to the filesystem path, adding separators
+// as necessary.
+func (fs FS) Path(p ...string) string {
+	return filepath.Join(append([]string{string(fs)}, p...)...)
+}
diff --git a/internal/util/parse.go b/internal/util/parse.go
index 1ad21c9..ca74889 100644
--- a/internal/util/parse.go
+++ b/internal/util/parse.go
@@ -13,7 +13,11 @@
 
 package util
 
-import "strconv"
+import (
+	"io/ioutil"
+	"strconv"
+	"strings"
+)
 
 // ParseUint32s parses a slice of strings into a slice of uint32s.
 func ParseUint32s(ss []string) ([]uint32, error) {
@@ -44,3 +48,26 @@ func ParseUint64s(ss []string) ([]uint64, error) {
 
 	return us, nil
 }
+
+// ReadUintFromFile reads a file and attempts to parse a uint64 from it.
+func ReadUintFromFile(path string) (uint64, error) {
+	data, err := ioutil.ReadFile(path)
+	if err != nil {
+		return 0, err
+	}
+	return strconv.ParseUint(strings.TrimSpace(string(data)), 10, 64)
+}
+
+// ParseBool parses a string into a boolean pointer.
+func ParseBool(b string) *bool {
+	var truth bool
+	switch b {
+	case "enabled":
+		truth = true
+	case "disabled":
+		truth = false
+	default:
+		return nil
+	}
+	return &truth
+}
diff --git a/internal/util/sysreadfile.go b/internal/util/sysreadfile.go
new file mode 100644
index 0000000..68b37c4
--- /dev/null
+++ b/internal/util/sysreadfile.go
@@ -0,0 +1,45 @@
+// Copyright 2018 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build linux,!appengine
+
+package util
+
+import (
+	"bytes"
+	"os"
+	"syscall"
+)
+
+// SysReadFile is a simplified ioutil.ReadFile that invokes syscall.Read directly.
+// https://github.com/prometheus/node_exporter/pull/728/files
+func SysReadFile(file string) (string, error) {
+	f, err := os.Open(file)
+	if err != nil {
+		return "", err
+	}
+	defer f.Close()
+
+	// On some machines, hwmon drivers are broken and return EAGAIN.  This causes
+	// Go's ioutil.ReadFile implementation to poll forever.
+	//
+	// Since we either want to read data or bail immediately, do the simplest
+	// possible read using syscall directly.
+	b := make([]byte, 128)
+	n, err := syscall.Read(int(f.Fd()), b)
+	if err != nil {
+		return "", err
+	}
+
+	return string(bytes.TrimSpace(b[:n])), nil
+}
diff --git a/internal/util/sysreadfile_compat.go b/internal/util/sysreadfile_compat.go
new file mode 100644
index 0000000..bd55b45
--- /dev/null
+++ b/internal/util/sysreadfile_compat.go
@@ -0,0 +1,26 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build linux,appengine !linux
+
+package util
+
+import (
+	"fmt"
+)
+
+// SysReadFile is here implemented as a noop for builds that do not support
+// the read syscall. For example Windows, or Linux on Google App Engine.
+func SysReadFile(file string) (string, error) {
+	return "", fmt.Errorf("not supported on this platform")
+}
diff --git a/ipvs.go b/ipvs.go
index e36d4a3..41e645d 100644
--- a/ipvs.go
+++ b/ipvs.go
@@ -74,7 +74,7 @@ func NewIPVSStats() (IPVSStats, error) {
 
 // NewIPVSStats reads the IPVS statistics from the specified `proc` filesystem.
 func (fs FS) NewIPVSStats() (IPVSStats, error) {
-	file, err := os.Open(fs.Path("net/ip_vs_stats"))
+	file, err := os.Open(fs.proc.Path("net/ip_vs_stats"))
 	if err != nil {
 		return IPVSStats{}, err
 	}
@@ -143,7 +143,7 @@ func NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
 
 // NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem.
 func (fs FS) NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
-	file, err := os.Open(fs.Path("net/ip_vs"))
+	file, err := os.Open(fs.proc.Path("net/ip_vs"))
 	if err != nil {
 		return nil, err
 	}
diff --git a/mdstat.go b/mdstat.go
index 9dc1958..6ac7a12 100644
--- a/mdstat.go
+++ b/mdstat.go
@@ -44,7 +44,7 @@ type MDStat struct {
 
 // ParseMDStat parses an mdstat-file and returns a struct with the relevant infos.
 func (fs FS) ParseMDStat() (mdstates []MDStat, err error) {
-	mdStatusFilePath := fs.Path("mdstat")
+	mdStatusFilePath := fs.proc.Path("mdstat")
 	content, err := ioutil.ReadFile(mdStatusFilePath)
 	if err != nil {
 		return []MDStat{}, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
diff --git a/mountstats.go b/mountstats.go
index e95ddbc..fc385af 100644
--- a/mountstats.go
+++ b/mountstats.go
@@ -39,8 +39,11 @@ const (
 	statVersion10 = "1.0"
 	statVersion11 = "1.1"
 
-	fieldTransport10Len = 10
-	fieldTransport11Len = 13
+	fieldTransport10TCPLen = 10
+	fieldTransport10UDPLen = 7
+
+	fieldTransport11TCPLen = 13
+	fieldTransport11UDPLen = 10
 )
 
 // A Mount is a device mount parsed from /proc/[pid]/mountstats.
@@ -66,6 +69,8 @@ type MountStats interface {
 type MountStatsNFS struct {
 	// The version of statistics provided.
 	StatVersion string
+	// The optional mountaddr of the NFS mount.
+	MountAddress string
 	// The age of the NFS mount.
 	Age time.Duration
 	// Statistics related to byte counters for various operations.
@@ -186,6 +191,8 @@ type NFSOperationStats struct {
 // A NFSTransportStats contains statistics for the NFS mount RPC requests and
 // responses.
 type NFSTransportStats struct {
+	// The transport protocol used for the NFS mount.
+	Protocol string
 	// The local port used for the NFS mount.
 	Port uint64
 	// Number of times the client has had to establish a connection from scratch
@@ -312,6 +319,7 @@ func parseMount(ss []string) (*Mount, error) {
 func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, error) {
 	// Field indicators for parsing specific types of data
 	const (
+		fieldOpts       = "opts:"
 		fieldAge        = "age:"
 		fieldBytes      = "bytes:"
 		fieldEvents     = "events:"
@@ -333,6 +341,13 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e
 		}
 
 		switch ss[0] {
+		case fieldOpts:
+			for _, opt := range strings.Split(ss[1], ",") {
+				split := strings.Split(opt, "=")
+				if len(split) == 2 && split[0] == "mountaddr" {
+					stats.MountAddress = split[1]
+				}
+			}
 		case fieldAge:
 			// Age integer is in seconds
 			d, err := time.ParseDuration(ss[1] + "s")
@@ -360,7 +375,7 @@ func parseMountStatsNFS(s *bufio.Scanner, statVersion string) (*MountStatsNFS, e
 				return nil, fmt.Errorf("not enough information for NFS transport stats: %v", ss)
 			}
 
-			tstats, err := parseNFSTransportStats(ss[2:], statVersion)
+			tstats, err := parseNFSTransportStats(ss[1:], statVersion)
 			if err != nil {
 				return nil, err
 			}
@@ -522,13 +537,33 @@ func parseNFSOperationStats(s *bufio.Scanner) ([]NFSOperationStats, error) {
 // parseNFSTransportStats parses a NFSTransportStats line using an input set of
 // integer fields matched to a specific stats version.
 func parseNFSTransportStats(ss []string, statVersion string) (*NFSTransportStats, error) {
+	// Extract the protocol field. It is the only string value in the line
+	protocol := ss[0]
+	ss = ss[1:]
+
 	switch statVersion {
 	case statVersion10:
-		if len(ss) != fieldTransport10Len {
+		var expectedLength int
+		if protocol == "tcp" {
+			expectedLength = fieldTransport10TCPLen
+		} else if protocol == "udp" {
+			expectedLength = fieldTransport10UDPLen
+		} else {
+			return nil, fmt.Errorf("invalid NFS protocol \"%s\" in stats 1.0 statement: %v", protocol, ss)
+		}
+		if len(ss) != expectedLength {
 			return nil, fmt.Errorf("invalid NFS transport stats 1.0 statement: %v", ss)
 		}
 	case statVersion11:
-		if len(ss) != fieldTransport11Len {
+		var expectedLength int
+		if protocol == "tcp" {
+			expectedLength = fieldTransport11TCPLen
+		} else if protocol == "udp" {
+			expectedLength = fieldTransport11UDPLen
+		} else {
+			return nil, fmt.Errorf("invalid NFS protocol \"%s\" in stats 1.1 statement: %v", protocol, ss)
+		}
+		if len(ss) != expectedLength {
 			return nil, fmt.Errorf("invalid NFS transport stats 1.1 statement: %v", ss)
 		}
 	default:
@@ -536,12 +571,13 @@ func parseNFSTransportStats(ss []string, statVersion string) (*NFSTransportStats
 	}
 
 	// Allocate enough for v1.1 stats since zero value for v1.1 stats will be okay
-	// in a v1.0 response.
+	// in a v1.0 response. Since the stat length is bigger for TCP stats, we use
+	// the TCP length here.
 	//
 	// Note: slice length must be set to length of v1.1 stats to avoid a panic when
 	// only v1.0 stats are present.
 	// See: https://github.com/prometheus/node_exporter/issues/571.
-	ns := make([]uint64, fieldTransport11Len)
+	ns := make([]uint64, fieldTransport11TCPLen)
 	for i, s := range ss {
 		n, err := strconv.ParseUint(s, 10, 64)
 		if err != nil {
@@ -551,7 +587,18 @@ func parseNFSTransportStats(ss []string, statVersion string) (*NFSTransportStats
 		ns[i] = n
 	}
 
+	// The fields differ depending on the transport protocol (TCP or UDP)
+	// From https://utcc.utoronto.ca/%7Ecks/space/blog/linux/NFSMountstatsXprt
+	//
+	// For the udp RPC transport there is no connection count, connect idle time,
+	// or idle time (fields #3, #4, and #5); all other fields are the same. So
+	// we set them to 0 here.
+	if protocol == "udp" {
+		ns = append(ns[:2], append(make([]uint64, 3), ns[2:]...)...)
+	}
+
 	return &NFSTransportStats{
+		Protocol:                 protocol,
 		Port:                     ns[0],
 		Bind:                     ns[1],
 		Connect:                  ns[2],
diff --git a/net_dev.go b/net_dev.go
index 3f25233..8249c98 100644
--- a/net_dev.go
+++ b/net_dev.go
@@ -59,7 +59,7 @@ func NewNetDev() (NetDev, error) {
 
 // NewNetDev returns kernel/system statistics read from /proc/net/dev.
 func (fs FS) NewNetDev() (NetDev, error) {
-	return newNetDev(fs.Path("net/dev"))
+	return newNetDev(fs.proc.Path("net/dev"))
 }
 
 // NewNetDev returns kernel/system statistics read from /proc/[pid]/net/dev.
@@ -75,7 +75,7 @@ func newNetDev(file string) (NetDev, error) {
 	}
 	defer f.Close()
 
-	nd := NetDev{}
+	netDev := NetDev{}
 	s := bufio.NewScanner(f)
 	for n := 0; s.Scan(); n++ {
 		// Skip the 2 header lines.
@@ -83,20 +83,20 @@ func newNetDev(file string) (NetDev, error) {
 			continue
 		}
 
-		line, err := nd.parseLine(s.Text())
+		line, err := netDev.parseLine(s.Text())
 		if err != nil {
-			return nd, err
+			return netDev, err
 		}
 
-		nd[line.Name] = *line
+		netDev[line.Name] = *line
 	}
 
-	return nd, s.Err()
+	return netDev, s.Err()
 }
 
 // parseLine parses a single line from the /proc/net/dev file. Header lines
 // must be filtered prior to calling this method.
-func (nd NetDev) parseLine(rawLine string) (*NetDevLine, error) {
+func (netDev NetDev) parseLine(rawLine string) (*NetDevLine, error) {
 	parts := strings.SplitN(rawLine, ":", 2)
 	if len(parts) != 2 {
 		return nil, errors.New("invalid net/dev line, missing colon")
@@ -185,11 +185,11 @@ func (nd NetDev) parseLine(rawLine string) (*NetDevLine, error) {
 
 // Total aggregates the values across interfaces and returns a new NetDevLine.
 // The Name field will be a sorted comma separated list of interface names.
-func (nd NetDev) Total() NetDevLine {
+func (netDev NetDev) Total() NetDevLine {
 	total := NetDevLine{}
 
-	names := make([]string, 0, len(nd))
-	for _, ifc := range nd {
+	names := make([]string, 0, len(netDev))
+	for _, ifc := range netDev {
 		names = append(names, ifc.Name)
 		total.RxBytes += ifc.RxBytes
 		total.RxPackets += ifc.RxPackets
diff --git a/nfs/nfs.go b/nfs/nfs.go
index 651bf68..7ee262f 100644
--- a/nfs/nfs.go
+++ b/nfs/nfs.go
@@ -15,6 +15,13 @@
 // Fields are documented in https://www.svennd.be/nfsd-stats-explained-procnetrpcnfsd/
 package nfs
 
+import (
+	"os"
+	"strings"
+
+	"github.com/prometheus/procfs/internal/fs"
+)
+
 // ReplyCache models the "rc" line.
 type ReplyCache struct {
 	Hits    uint64
@@ -261,3 +268,46 @@ type ServerRPCStats struct {
 	ServerV4Stats  ServerV4Stats
 	V4Ops          V4Ops
 }
+
+// FS represents the pseudo-filesystem proc, which provides an interface to
+// kernel data structures.
+type FS struct {
+	proc *fs.FS
+}
+
+// NewFS returns a new FS mounted under the given mountPoint. It will error
+// if the mount point can't be read.
+func NewFS(mountPoint string) (FS, error) {
+	if strings.TrimSpace(mountPoint) == "" {
+		mountPoint = fs.DefaultProcMountPoint
+	}
+	fs, err := fs.NewFS(mountPoint)
+	if err != nil {
+		return FS{}, err
+	}
+	return FS{&fs}, nil
+}
+
+// ClientRPCStats retrieves NFS client RPC statistics
+// from proc/net/rpc/nfs.
+func (fs FS) ClientRPCStats() (*ClientRPCStats, error) {
+	f, err := os.Open(fs.proc.Path("net/rpc/nfs"))
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+
+	return ParseClientRPCStats(f)
+}
+
+// ServerRPCStats retrieves NFS daemon RPC statistics
+// from proc/net/rpc/nfsd.
+func (fs FS) ServerRPCStats() (*ServerRPCStats, error) {
+	f, err := os.Open(fs.proc.Path("net/rpc/nfsd"))
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+
+	return ParseServerRPCStats(f)
+}
diff --git a/proc.go b/proc.go
index 7cf5b8a..8e38493 100644
--- a/proc.go
+++ b/proc.go
@@ -20,6 +20,8 @@ import (
 	"os"
 	"strconv"
 	"strings"
+
+	"github.com/prometheus/procfs/internal/fs"
 )
 
 // Proc provides information about a running process.
@@ -27,7 +29,7 @@ type Proc struct {
 	// The process ID.
 	PID int
 
-	fs FS
+	fs fs.FS
 }
 
 // Procs represents a list of Proc structs.
@@ -66,11 +68,11 @@ func AllProcs() (Procs, error) {
 
 // Self returns a process for the current process.
 func (fs FS) Self() (Proc, error) {
-	p, err := os.Readlink(fs.Path("self"))
+	p, err := os.Readlink(fs.proc.Path("self"))
 	if err != nil {
 		return Proc{}, err
 	}
-	pid, err := strconv.Atoi(strings.Replace(p, string(fs), "", -1))
+	pid, err := strconv.Atoi(strings.Replace(p, string(fs.proc), "", -1))
 	if err != nil {
 		return Proc{}, err
 	}
@@ -79,15 +81,15 @@ func (fs FS) Self() (Proc, error) {
 
 // NewProc returns a process for the given pid.
 func (fs FS) NewProc(pid int) (Proc, error) {
-	if _, err := os.Stat(fs.Path(strconv.Itoa(pid))); err != nil {
+	if _, err := os.Stat(fs.proc.Path(strconv.Itoa(pid))); err != nil {
 		return Proc{}, err
 	}
-	return Proc{PID: pid, fs: fs}, nil
+	return Proc{PID: pid, fs: fs.proc}, nil
 }
 
 // AllProcs returns a list of all currently available processes.
 func (fs FS) AllProcs() (Procs, error) {
-	d, err := os.Open(fs.Path())
+	d, err := os.Open(fs.proc.Path())
 	if err != nil {
 		return Procs{}, err
 	}
@@ -104,7 +106,7 @@ func (fs FS) AllProcs() (Procs, error) {
 		if err != nil {
 			continue
 		}
-		p = append(p, Proc{PID: int(pid), fs: fs})
+		p = append(p, Proc{PID: int(pid), fs: fs.proc})
 	}
 
 	return p, nil
@@ -156,6 +158,26 @@ func (p Proc) Executable() (string, error) {
 	return exe, err
 }
 
+// Cwd returns the absolute path to the current working directory of the process.
+func (p Proc) Cwd() (string, error) {
+	wd, err := os.Readlink(p.path("cwd"))
+	if os.IsNotExist(err) {
+		return "", nil
+	}
+
+	return wd, err
+}
+
+// RootDir returns the absolute path to the process's root directory (as set by chroot)
+func (p Proc) RootDir() (string, error) {
+	rdir, err := os.Readlink(p.path("root"))
+	if os.IsNotExist(err) {
+		return "", nil
+	}
+
+	return rdir, err
+}
+
 // FileDescriptors returns the currently open file descriptors of a process.
 func (p Proc) FileDescriptors() ([]uintptr, error) {
 	names, err := p.fileDescriptors()
diff --git a/proc_psi.go b/proc_psi.go
new file mode 100644
index 0000000..a23d4c0
--- /dev/null
+++ b/proc_psi.go
@@ -0,0 +1,110 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+// The PSI / pressure interface is described at
+//   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/accounting/psi.txt
+// Each resource (cpu, io, memory, ...) is exposed as a single file.
+// Each file may contain up to two lines, one for "some" pressure and one for "full" pressure.
+// Each line contains several averages (over n seconds) and a total in µs.
+//
+// Example io pressure file:
+// > some avg10=0.06 avg60=0.21 avg300=0.99 total=8537362
+// > full avg10=0.00 avg60=0.13 avg300=0.96 total=8183134
+
+import (
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"strings"
+)
+
+const lineFormat = "avg10=%f avg60=%f avg300=%f total=%d"
+
+// PSILine is a single line of values as returned by /proc/pressure/*
+// The Avg entries are averages over n seconds, as a percentage
+// The Total line is in microseconds
+type PSILine struct {
+	Avg10  float64
+	Avg60  float64
+	Avg300 float64
+	Total  uint64
+}
+
+// PSIStats represent pressure stall information from /proc/pressure/*
+// Some indicates the share of time in which at least some tasks are stalled
+// Full indicates the share of time in which all non-idle tasks are stalled simultaneously
+type PSIStats struct {
+	Some *PSILine
+	Full *PSILine
+}
+
+// NewPSIStatsForResource reads pressure stall information for the specified
+// resource. At time of writing this can be either "cpu", "memory" or "io".
+func NewPSIStatsForResource(resource string) (PSIStats, error) {
+	fs, err := NewFS(DefaultMountPoint)
+	if err != nil {
+		return PSIStats{}, err
+	}
+
+	return fs.NewPSIStatsForResource(resource)
+}
+
+// NewPSIStatsForResource reads pressure stall information from /proc/pressure/<resource>
+func (fs FS) NewPSIStatsForResource(resource string) (PSIStats, error) {
+	file, err := os.Open(fs.proc.Path(fmt.Sprintf("%s/%s", "pressure", resource)))
+	if err != nil {
+		return PSIStats{}, fmt.Errorf("psi_stats: unavailable for %s", resource)
+	}
+
+	defer file.Close()
+	return parsePSIStats(resource, file)
+}
+
+// parsePSIStats parses the specified file for pressure stall information
+func parsePSIStats(resource string, file io.Reader) (PSIStats, error) {
+	psiStats := PSIStats{}
+	stats, err := ioutil.ReadAll(file)
+	if err != nil {
+		return psiStats, fmt.Errorf("psi_stats: unable to read data for %s", resource)
+	}
+
+	for _, l := range strings.Split(string(stats), "\n") {
+		prefix := strings.Split(l, " ")[0]
+		switch prefix {
+		case "some":
+			psi := PSILine{}
+			_, err := fmt.Sscanf(l, fmt.Sprintf("some %s", lineFormat), &psi.Avg10, &psi.Avg60, &psi.Avg300, &psi.Total)
+			if err != nil {
+				return PSIStats{}, err
+			}
+			psiStats.Some = &psi
+		case "full":
+			psi := PSILine{}
+			_, err := fmt.Sscanf(l, fmt.Sprintf("full %s", lineFormat), &psi.Avg10, &psi.Avg60, &psi.Avg300, &psi.Total)
+			if err != nil {
+				return PSIStats{}, err
+			}
+			psiStats.Full = &psi
+		default:
+			// If we encounter a line with an unknown prefix, ignore it and move on
+			// Should new measurement types be added in the future we'll simply ignore them instead
+			// of erroring on retrieval
+			continue
+		}
+	}
+
+	return psiStats, nil
+}
diff --git a/proc_stat.go b/proc_stat.go
index 3cf2a9f..4c8b03c 100644
--- a/proc_stat.go
+++ b/proc_stat.go
@@ -18,6 +18,8 @@ import (
 	"fmt"
 	"io/ioutil"
 	"os"
+
+	"github.com/prometheus/procfs/internal/fs"
 )
 
 // Originally, this USER_HZ value was dynamically retrieved via a sysconf call
@@ -95,11 +97,11 @@ type ProcStat struct {
 	// in clock ticks.
 	Starttime uint64
 	// Virtual memory size in bytes.
-	VSize int
+	VSize uint
 	// Resident set size in pages.
 	RSS int
 
-	fs FS
+	proc fs.FS
 }
 
 // NewStat returns the current status information of the process.
@@ -118,7 +120,7 @@ func (p Proc) NewStat() (ProcStat, error) {
 	var (
 		ignore int
 
-		s = ProcStat{PID: p.PID, fs: p.fs}
+		s = ProcStat{PID: p.PID, proc: p.fs}
 		l = bytes.Index(data, []byte("("))
 		r = bytes.LastIndex(data, []byte(")"))
 	)
@@ -164,7 +166,7 @@ func (p Proc) NewStat() (ProcStat, error) {
 }
 
 // VirtualMemory returns the virtual memory size in bytes.
-func (s ProcStat) VirtualMemory() int {
+func (s ProcStat) VirtualMemory() uint {
 	return s.VSize
 }
 
@@ -175,7 +177,8 @@ func (s ProcStat) ResidentMemory() int {
 
 // StartTime returns the unix timestamp of the process in seconds.
 func (s ProcStat) StartTime() (float64, error) {
-	stat, err := s.fs.NewStat()
+	fs := FS{proc: s.proc}
+	stat, err := fs.NewStat()
 	if err != nil {
 		return 0, err
 	}
diff --git a/proc_status.go b/proc_status.go
new file mode 100644
index 0000000..6b4b61f
--- /dev/null
+++ b/proc_status.go
@@ -0,0 +1,162 @@
+// Copyright 2018 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"bytes"
+	"io/ioutil"
+	"os"
+	"strconv"
+	"strings"
+)
+
+// ProcStat provides status information about the process,
+// read from /proc/[pid]/stat.
+type ProcStatus struct {
+	// The process ID.
+	PID int
+	// The process name.
+	Name string
+
+	// Peak virtual memory size.
+	VmPeak uint64
+	// Virtual memory size.
+	VmSize uint64
+	// Locked memory size.
+	VmLck uint64
+	// Pinned memory size.
+	VmPin uint64
+	// Peak resident set size.
+	VmHWM uint64
+	// Resident set size (sum of RssAnnon RssFile and RssShmem).
+	VmRSS uint64
+	// Size of resident anonymous memory.
+	RssAnon uint64
+	// Size of resident file mappings.
+	RssFile uint64
+	// Size of resident shared memory.
+	RssShmem uint64
+	// Size of data segments.
+	VmData uint64
+	// Size of stack segments.
+	VmStk uint64
+	// Size of text segments.
+	VmExe uint64
+	// Shared library code size.
+	VmLib uint64
+	// Page table entries size.
+	VmPTE uint64
+	// Size of second-level page tables.
+	VmPMD uint64
+	// Swapped-out virtual memory size by anonymous private.
+	VmSwap uint64
+	// Size of hugetlb memory portions
+	HugetlbPages uint64
+
+	// Number of voluntary context switches.
+	VoluntaryCtxtSwitches uint64
+	// Number of involuntary context switches.
+	NonVoluntaryCtxtSwitches uint64
+}
+
+// NewStatus returns the current status information of the process.
+func (p Proc) NewStatus() (ProcStatus, error) {
+	f, err := os.Open(p.path("status"))
+	if err != nil {
+		return ProcStatus{}, err
+	}
+	defer f.Close()
+
+	data, err := ioutil.ReadAll(f)
+	if err != nil {
+		return ProcStatus{}, err
+	}
+
+	s := ProcStatus{PID: p.PID}
+
+	lines := strings.Split(string(data), "\n")
+	for _, line := range lines {
+		if !bytes.Contains([]byte(line), []byte(":")) {
+			continue
+		}
+
+		kv := strings.SplitN(line, ":", 2)
+
+		// removes spaces
+		k := string(strings.TrimSpace(kv[0]))
+		v := string(strings.TrimSpace(kv[1]))
+		// removes "kB"
+		v = string(bytes.Trim([]byte(v), " kB"))
+
+		// value to int when possible
+		// we can skip error check here, 'cause vKBytes is not used when value is a string
+		vKBytes, _ := strconv.ParseUint(v, 10, 64)
+		// convert kB to B
+		vBytes := vKBytes * 1024
+
+		s.fillStatus(k, v, vKBytes, vBytes)
+	}
+
+	return s, nil
+}
+
+func (s *ProcStatus) fillStatus(k string, vString string, vUint uint64, vUintBytes uint64) {
+	switch k {
+	case "Name":
+		s.Name = vString
+	case "VmPeak":
+		s.VmPeak = vUintBytes
+	case "VmSize":
+		s.VmSize = vUintBytes
+	case "VmLck":
+		s.VmLck = vUintBytes
+	case "VmPin":
+		s.VmPin = vUintBytes
+	case "VmHWM":
+		s.VmHWM = vUintBytes
+	case "VmRSS":
+		s.VmRSS = vUintBytes
+	case "RssAnon":
+		s.RssAnon = vUintBytes
+	case "RssFile":
+		s.RssFile = vUintBytes
+	case "RssShmem":
+		s.RssShmem = vUintBytes
+	case "VmData":
+		s.VmData = vUintBytes
+	case "VmStk":
+		s.VmStk = vUintBytes
+	case "VmExe":
+		s.VmExe = vUintBytes
+	case "VmLib":
+		s.VmLib = vUintBytes
+	case "VmPTE":
+		s.VmPTE = vUintBytes
+	case "VmPMD":
+		s.VmPMD = vUintBytes
+	case "VmSwap":
+		s.VmSwap = vUintBytes
+	case "HugetlbPages":
+		s.HugetlbPages = vUintBytes
+	case "voluntary_ctxt_switches":
+		s.VoluntaryCtxtSwitches = vUint
+	case "nonvoluntary_ctxt_switches":
+		s.NonVoluntaryCtxtSwitches = vUint
+	}
+}
+
+// TotalCtxtSwitches returns the total context switch.
+func (s ProcStatus) TotalCtxtSwitches() uint64 {
+	return s.VoluntaryCtxtSwitches + s.NonVoluntaryCtxtSwitches
+}
diff --git a/stat.go b/stat.go
index 61eb6b0..44c9af1 100644
--- a/stat.go
+++ b/stat.go
@@ -153,7 +153,7 @@ func parseSoftIRQStat(line string) (SoftIRQStat, uint64, error) {
 func (fs FS) NewStat() (Stat, error) {
 	// See https://www.kernel.org/doc/Documentation/filesystems/proc.txt
 
-	f, err := os.Open(fs.Path("stat"))
+	f, err := os.Open(fs.proc.Path("stat"))
 	if err != nil {
 		return Stat{}, err
 	}
diff --git a/sysfs/class_power_supply.go b/sysfs/class_power_supply.go
new file mode 100644
index 0000000..64720ef
--- /dev/null
+++ b/sysfs/class_power_supply.go
@@ -0,0 +1,188 @@
+// Copyright 2018 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build !windows
+
+package sysfs
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"reflect"
+	"strconv"
+	"strings"
+
+	"github.com/prometheus/procfs/internal/util"
+)
+
+// PowerSupply contains info from files in /sys/class/power_supply for a single power supply.
+type PowerSupply struct {
+	Name                     string // Power Supply Name
+	Authentic                *int64 `fileName:"authentic"`                   // /sys/class/power_suppy/<Name>/authentic
+	Calibrate                *int64 `fileName:"calibrate"`                   // /sys/class/power_suppy/<Name>/calibrate
+	Capacity                 *int64 `fileName:"capacity"`                    // /sys/class/power_suppy/<Name>/capacity
+	CapacityAlertMax         *int64 `fileName:"capacity_alert_max"`          // /sys/class/power_suppy/<Name>/capacity_alert_max
+	CapacityAlertMin         *int64 `fileName:"capacity_alert_min"`          // /sys/class/power_suppy/<Name>/capacity_alert_min
+	CapacityLevel            string `fileName:"capacity_level"`              // /sys/class/power_suppy/<Name>/capacity_level
+	ChargeAvg                *int64 `fileName:"charge_avg"`                  // /sys/class/power_suppy/<Name>/charge_avg
+	ChargeControlLimit       *int64 `fileName:"charge_control_limit"`        // /sys/class/power_suppy/<Name>/charge_control_limit
+	ChargeControlLimitMax    *int64 `fileName:"charge_control_limit_max"`    // /sys/class/power_suppy/<Name>/charge_control_limit_max
+	ChargeCounter            *int64 `fileName:"charge_counter"`              // /sys/class/power_suppy/<Name>/charge_counter
+	ChargeEmpty              *int64 `fileName:"charge_empty"`                // /sys/class/power_suppy/<Name>/charge_empty
+	ChargeEmptyDesign        *int64 `fileName:"charge_empty_design"`         // /sys/class/power_suppy/<Name>/charge_empty_design
+	ChargeFull               *int64 `fileName:"charge_full"`                 // /sys/class/power_suppy/<Name>/charge_full
+	ChargeFullDesign         *int64 `fileName:"charge_full_design"`          // /sys/class/power_suppy/<Name>/charge_full_design
+	ChargeNow                *int64 `fileName:"charge_now"`                  // /sys/class/power_suppy/<Name>/charge_now
+	ChargeTermCurrent        *int64 `fileName:"charge_term_current"`         // /sys/class/power_suppy/<Name>/charge_term_current
+	ChargeType               string `fileName:"charge_type"`                 // /sys/class/power_supply/<Name>/charge_type
+	ConstantChargeCurrent    *int64 `fileName:"constant_charge_current"`     // /sys/class/power_suppy/<Name>/constant_charge_current
+	ConstantChargeCurrentMax *int64 `fileName:"constant_charge_current_max"` // /sys/class/power_suppy/<Name>/constant_charge_current_max
+	ConstantChargeVoltage    *int64 `fileName:"constant_charge_voltage"`     // /sys/class/power_suppy/<Name>/constant_charge_voltage
+	ConstantChargeVoltageMax *int64 `fileName:"constant_charge_voltage_max"` // /sys/class/power_suppy/<Name>/constant_charge_voltage_max
+	CurrentAvg               *int64 `fileName:"current_avg"`                 // /sys/class/power_suppy/<Name>/current_avg
+	CurrentBoot              *int64 `fileName:"current_boot"`                // /sys/class/power_suppy/<Name>/current_boot
+	CurrentMax               *int64 `fileName:"current_max"`                 // /sys/class/power_suppy/<Name>/current_max
+	CurrentNow               *int64 `fileName:"current_now"`                 // /sys/class/power_suppy/<Name>/current_now
+	CycleCount               *int64 `fileName:"cycle_count"`                 // /sys/class/power_suppy/<Name>/cycle_count
+	EnergyAvg                *int64 `fileName:"energy_avg"`                  // /sys/class/power_supply/<Name>/energy_avg
+	EnergyEmpty              *int64 `fileName:"energy_empty"`                // /sys/class/power_suppy/<Name>/energy_empty
+	EnergyEmptyDesign        *int64 `fileName:"energy_empty_design"`         // /sys/class/power_suppy/<Name>/energy_empty_design
+	EnergyFull               *int64 `fileName:"energy_full"`                 // /sys/class/power_suppy/<Name>/energy_full
+	EnergyFullDesign         *int64 `fileName:"energy_full_design"`          // /sys/class/power_suppy/<Name>/energy_full_design
+	EnergyNow                *int64 `fileName:"energy_now"`                  // /sys/class/power_supply/<Name>/energy_now
+	Health                   string `fileName:"health"`                      // /sys/class/power_suppy/<Name>/health
+	InputCurrentLimit        *int64 `fileName:"input_current_limit"`         // /sys/class/power_suppy/<Name>/input_current_limit
+	Manufacturer             string `fileName:"manufacturer"`                // /sys/class/power_suppy/<Name>/manufacturer
+	ModelName                string `fileName:"model_name"`                  // /sys/class/power_suppy/<Name>/model_name
+	Online                   *int64 `fileName:"online"`                      // /sys/class/power_suppy/<Name>/online
+	PowerAvg                 *int64 `fileName:"power_avg"`                   // /sys/class/power_suppy/<Name>/power_avg
+	PowerNow                 *int64 `fileName:"power_now"`                   // /sys/class/power_suppy/<Name>/power_now
+	PrechargeCurrent         *int64 `fileName:"precharge_current"`           // /sys/class/power_suppy/<Name>/precharge_current
+	Present                  *int64 `fileName:"present"`                     // /sys/class/power_suppy/<Name>/present
+	Scope                    string `fileName:"scope"`                       // /sys/class/power_suppy/<Name>/scope
+	SerialNumber             string `fileName:"serial_number"`               // /sys/class/power_suppy/<Name>/serial_number
+	Status                   string `fileName:"status"`                      // /sys/class/power_supply/<Name>/status
+	Technology               string `fileName:"technology"`                  // /sys/class/power_suppy/<Name>/technology
+	Temp                     *int64 `fileName:"temp"`                        // /sys/class/power_suppy/<Name>/temp
+	TempAlertMax             *int64 `fileName:"temp_alert_max"`              // /sys/class/power_suppy/<Name>/temp_alert_max
+	TempAlertMin             *int64 `fileName:"temp_alert_min"`              // /sys/class/power_suppy/<Name>/temp_alert_min
+	TempAmbient              *int64 `fileName:"temp_ambient"`                // /sys/class/power_suppy/<Name>/temp_ambient
+	TempAmbientMax           *int64 `fileName:"temp_ambient_max"`            // /sys/class/power_suppy/<Name>/temp_ambient_max
+	TempAmbientMin           *int64 `fileName:"temp_ambient_min"`            // /sys/class/power_suppy/<Name>/temp_ambient_min
+	TempMax                  *int64 `fileName:"temp_max"`                    // /sys/class/power_suppy/<Name>/temp_max
+	TempMin                  *int64 `fileName:"temp_min"`                    // /sys/class/power_suppy/<Name>/temp_min
+	TimeToEmptyAvg           *int64 `fileName:"time_to_empty_avg"`           // /sys/class/power_suppy/<Name>/time_to_empty_avg
+	TimeToEmptyNow           *int64 `fileName:"time_to_empty_now"`           // /sys/class/power_suppy/<Name>/time_to_empty_now
+	TimeToFullAvg            *int64 `fileName:"time_to_full_avg"`            // /sys/class/power_suppy/<Name>/time_to_full_avg
+	TimeToFullNow            *int64 `fileName:"time_to_full_now"`            // /sys/class/power_suppy/<Name>/time_to_full_now
+	Type                     string `fileName:"type"`                        // /sys/class/power_supply/<Name>/type
+	UsbType                  string `fileName:"usb_type"`                    // /sys/class/power_supply/<Name>/usb_type
+	VoltageAvg               *int64 `fileName:"voltage_avg"`                 // /sys/class/power_supply/<Name>/voltage_avg
+	VoltageBoot              *int64 `fileName:"voltage_boot"`                // /sys/class/power_suppy/<Name>/voltage_boot
+	VoltageMax               *int64 `fileName:"voltage_max"`                 // /sys/class/power_suppy/<Name>/voltage_max
+	VoltageMaxDesign         *int64 `fileName:"voltage_max_design"`          // /sys/class/power_suppy/<Name>/voltage_max_design
+	VoltageMin               *int64 `fileName:"voltage_min"`                 // /sys/class/power_suppy/<Name>/voltage_min
+	VoltageMinDesign         *int64 `fileName:"voltage_min_design"`          // /sys/class/power_suppy/<Name>/voltage_min_design
+	VoltageNow               *int64 `fileName:"voltage_now"`                 // /sys/class/power_supply/<Name>/voltage_now
+	VoltageOCV               *int64 `fileName:"voltage_ocv"`                 // /sys/class/power_suppy/<Name>/voltage_ocv
+}
+
+// PowerSupplyClass is a collection of every power supply in /sys/class/power_supply/.
+// The map keys are the names of the power supplies.
+type PowerSupplyClass map[string]PowerSupply
+
+// NewPowerSupplyClass returns info for all power supplies read from /sys/class/power_supply/.
+func NewPowerSupplyClass() (PowerSupplyClass, error) {
+	fs, err := NewFS(DefaultMountPoint)
+	if err != nil {
+		return nil, err
+	}
+
+	return fs.NewPowerSupplyClass()
+}
+
+// NewPowerSupplyClass returns info for all power supplies read from /sys/class/power_supply/.
+func (fs FS) NewPowerSupplyClass() (PowerSupplyClass, error) {
+	path := fs.sys.Path("class/power_supply")
+
+	powerSupplyDirs, err := ioutil.ReadDir(path)
+	if err != nil {
+		return PowerSupplyClass{}, fmt.Errorf("cannot access %s dir %s", path, err)
+	}
+
+	powerSupplyClass := PowerSupplyClass{}
+	for _, powerSupplyDir := range powerSupplyDirs {
+		powerSupply, err := powerSupplyClass.parsePowerSupply(path + "/" + powerSupplyDir.Name())
+		if err != nil {
+			return nil, err
+		}
+		powerSupply.Name = powerSupplyDir.Name()
+		powerSupplyClass[powerSupplyDir.Name()] = *powerSupply
+	}
+	return powerSupplyClass, nil
+}
+
+func (psc PowerSupplyClass) parsePowerSupply(powerSupplyPath string) (*PowerSupply, error) {
+	powerSupply := PowerSupply{}
+	powerSupplyElem := reflect.ValueOf(&powerSupply).Elem()
+	powerSupplyType := reflect.TypeOf(powerSupply)
+
+	//start from 1 - skip the Name field
+	for i := 1; i < powerSupplyElem.NumField(); i++ {
+		fieldType := powerSupplyType.Field(i)
+		fieldValue := powerSupplyElem.Field(i)
+
+		if fieldType.Tag.Get("fileName") == "" {
+			panic(fmt.Errorf("field %s does not have a filename tag", fieldType.Name))
+		}
+
+		value, err := util.SysReadFile(powerSupplyPath + "/" + fieldType.Tag.Get("fileName"))
+
+		if err != nil {
+			if os.IsNotExist(err) || err.Error() == "operation not supported" || err.Error() == "invalid argument" {
+				continue
+			}
+			return nil, fmt.Errorf("could not access file %s: %s", fieldType.Tag.Get("fileName"), err)
+		}
+
+		switch fieldValue.Kind() {
+		case reflect.String:
+			fieldValue.SetString(value)
+		case reflect.Ptr:
+			var int64ptr *int64
+			switch fieldValue.Type() {
+			case reflect.TypeOf(int64ptr):
+				var intValue int64
+				if strings.HasPrefix(value, "0x") {
+					intValue, err = strconv.ParseInt(value[2:], 16, 64)
+					if err != nil {
+						return nil, fmt.Errorf("expected hex value for %s, got: %s", fieldType.Name, value)
+					}
+				} else {
+					intValue, err = strconv.ParseInt(value, 10, 64)
+					if err != nil {
+						return nil, fmt.Errorf("expected Uint64 value for %s, got: %s", fieldType.Name, value)
+					}
+				}
+				fieldValue.Set(reflect.ValueOf(&intValue))
+			default:
+				return nil, fmt.Errorf("unhandled pointer type %q", fieldValue.Type())
+			}
+		default:
+			return nil, fmt.Errorf("unhandled type %q", fieldValue.Kind())
+		}
+	}
+
+	return &powerSupply, nil
+}
diff --git a/sysfs/class_thermal.go b/sysfs/class_thermal.go
new file mode 100644
index 0000000..2d70795
--- /dev/null
+++ b/sysfs/class_thermal.go
@@ -0,0 +1,99 @@
+// Copyright 2018 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build !windows
+
+package sysfs
+
+import (
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/prometheus/procfs/internal/util"
+)
+
+// ClassThermalZoneStats contains info from files in /sys/class/thermal/thermal_zone<zone>
+// for a single <zone>.
+// https://www.kernel.org/doc/Documentation/thermal/sysfs-api.txt
+type ClassThermalZoneStats struct {
+	Name    string  // The name of the zone from the directory structure.
+	Type    string  // The type of thermal zone.
+	Temp    uint64  // Temperature in millidegree Celsius.
+	Policy  string  // One of the various thermal governors used for a particular zone.
+	Mode    *bool   // Optional: One of the predefined values in [enabled, disabled].
+	Passive *uint64 // Optional: millidegrees Celsius. (0 for disabled, > 1000 for enabled+value)
+}
+
+// NewClassThermalZoneStats returns Thermal Zone metrics for all zones.
+func (fs FS) NewClassThermalZoneStats() ([]ClassThermalZoneStats, error) {
+	zones, err := filepath.Glob(fs.sys.Path("class/thermal/thermal_zone[0-9]*"))
+	if err != nil {
+		return []ClassThermalZoneStats{}, err
+	}
+
+	var zoneStats = ClassThermalZoneStats{}
+	stats := make([]ClassThermalZoneStats, len(zones))
+	for i, zone := range zones {
+		zoneName := strings.TrimPrefix(filepath.Base(zone), "thermal_zone")
+
+		zoneStats, err = parseClassThermalZone(zone)
+		if err != nil {
+			return []ClassThermalZoneStats{}, err
+		}
+		zoneStats.Name = zoneName
+		stats[i] = zoneStats
+	}
+	return stats, nil
+}
+
+func parseClassThermalZone(zone string) (ClassThermalZoneStats, error) {
+	// Required attributes.
+	zoneType, err := util.SysReadFile(filepath.Join(zone, "type"))
+	if err != nil {
+		return ClassThermalZoneStats{}, err
+	}
+	zonePolicy, err := util.SysReadFile(filepath.Join(zone, "policy"))
+	if err != nil {
+		return ClassThermalZoneStats{}, err
+	}
+	zoneTemp, err := util.ReadUintFromFile(filepath.Join(zone, "temp"))
+	if err != nil {
+		return ClassThermalZoneStats{}, err
+	}
+
+	// Optional attributes.
+	mode, err := util.SysReadFile(filepath.Join(zone, "mode"))
+	if err != nil && !os.IsNotExist(err) && !os.IsPermission(err) {
+		return ClassThermalZoneStats{}, err
+	}
+	zoneMode := util.ParseBool(mode)
+
+	var zonePassive *uint64
+	passive, err := util.ReadUintFromFile(filepath.Join(zone, "passive"))
+	if os.IsNotExist(err) || os.IsPermission(err) {
+		zonePassive = nil
+	} else if err != nil {
+		return ClassThermalZoneStats{}, err
+	} else {
+		zonePassive = &passive
+	}
+
+	return ClassThermalZoneStats{
+		Type:    zoneType,
+		Policy:  zonePolicy,
+		Temp:    zoneTemp,
+		Mode:    zoneMode,
+		Passive: zonePassive,
+	}, nil
+}
diff --git a/sysfs/fixtures.ttar b/sysfs/fixtures.ttar
deleted file mode 100644
index 8e665ce..0000000
--- a/sysfs/fixtures.ttar
+++ /dev/null
@@ -1,851 +0,0 @@
-# Archive created by ttar -c -f fixtures.ttar fixtures/
-Directory: fixtures
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata4
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/dirty_data
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/bypassed
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/cache_bypass_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/cache_bypass_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/cache_hit_ratio
-Lines: 1
-100
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/cache_hits
-Lines: 1
-289
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/cache_miss_collisions
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/cache_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_day/cache_readaheads
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/bypassed
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/cache_bypass_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/cache_bypass_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/cache_hit_ratio
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/cache_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/cache_miss_collisions
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/cache_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_five_minute/cache_readaheads
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/bypassed
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/cache_bypass_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/cache_bypass_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/cache_hit_ratio
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/cache_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/cache_miss_collisions
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/cache_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_hour/cache_readaheads
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/bypassed
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/cache_bypass_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/cache_bypass_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/cache_hit_ratio
-Lines: 1
-100
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/cache_hits
-Lines: 1
-546
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/cache_miss_collisions
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/cache_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata4/host3/target3:0:0/3:0:0:0/block/sdb/bcache/stats_total/cache_readaheads
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata5
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata5/host4
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0/block
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0/block/sdc
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0/block/sdc/bcache
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0/block/sdc/bcache/io_errors
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0/block/sdc/bcache/metadata_written
-Lines: 1
-512
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0/block/sdc/bcache/priority_stats
-Lines: 5
-Unused:		99%
-Metadata:	0%
-Average:	10473
-Sectors per Q:	64
-Quantiles:	[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946]
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/devices/pci0000:00/0000:00:0d.0/ata5/host4/target4:0:0/4:0:0:0/block/sdc/bcache/written
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/bcache
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/average_key_size
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0
-Mode: 777
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/dirty_data
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/bypassed
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/cache_bypass_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/cache_bypass_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/cache_hit_ratio
-Lines: 1
-100
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/cache_hits
-Lines: 1
-289
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/cache_miss_collisions
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/cache_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_day/cache_readaheads
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/bypassed
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/cache_bypass_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/cache_bypass_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/cache_hit_ratio
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/cache_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/cache_miss_collisions
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/cache_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_five_minute/cache_readaheads
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/bypassed
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/cache_bypass_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/cache_bypass_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/cache_hit_ratio
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/cache_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/cache_miss_collisions
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/cache_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_hour/cache_readaheads
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/bypassed
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/cache_bypass_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/cache_bypass_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/cache_hit_ratio
-Lines: 1
-100
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/cache_hits
-Lines: 1
-546
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/cache_miss_collisions
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/cache_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/bdev0/stats_total/cache_readaheads
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/btree_cache_size
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/cache0
-Mode: 777
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/cache0/io_errors
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/cache0/metadata_written
-Lines: 1
-512
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/cache0/priority_stats
-Lines: 5
-Unused:		99%
-Metadata:	0%
-Average:	10473
-Sectors per Q:	64
-Quantiles:	[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946 20946]
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/cache0/written
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/cache_available_percent
-Lines: 1
-100
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/congested
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/internal
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/internal/active_journal_entries
-Lines: 1
-1
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/internal/btree_nodes
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/internal/btree_read_average_duration_us
-Lines: 1
-1305
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/internal/cache_read_races
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/root_usage_percent
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/bypassed
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/cache_bypass_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/cache_bypass_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/cache_hit_ratio
-Lines: 1
-100
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/cache_hits
-Lines: 1
-289
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/cache_miss_collisions
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/cache_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_day/cache_readaheads
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/bypassed
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/cache_bypass_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/cache_bypass_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/cache_hit_ratio
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/cache_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/cache_miss_collisions
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/cache_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_five_minute/cache_readaheads
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/bypassed
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/cache_bypass_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/cache_bypass_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/cache_hit_ratio
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/cache_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/cache_miss_collisions
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/cache_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_hour/cache_readaheads
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/bypassed
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/cache_bypass_hits
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/cache_bypass_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/cache_hit_ratio
-Lines: 1
-100
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/cache_hits
-Lines: 1
-546
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/cache_miss_collisions
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/cache_misses
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/stats_total/cache_readaheads
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/bcache/deaddd54-c735-46d5-868e-f331c5fd7c74/tree_depth
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/xfs
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/xfs/sda1
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/xfs/sda1/stats
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/xfs/sda1/stats/stats
-Lines: 1
-extent_alloc 1 0 0 0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/xfs/sdb1
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/fs/xfs/sdb1/stats
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/fs/xfs/sdb1/stats/stats
-Lines: 1
-extent_alloc 2 0 0 0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Directory: fixtures/class/net/eth0/
-Mode: 755
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/addr_assign_type
-Lines: 1
-3
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/addr_len
-Lines: 1
-6
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/address
-Lines: 1
-01:01:01:01:01:01
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/broadcast
-Lines: 1
-ff:ff:ff:ff:ff:ff
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/carrier
-Lines: 1
-1
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/carrier_changes
-Lines: 1
-2
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/carrier_down_count
-Lines: 1
-1
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/carrier_up_count
-Lines: 1
-1
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/dev_id
-Lines: 1
-0x20
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/dormant
-Lines: 1
-1
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/duplex
-Lines: 1
-full
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/flags
-Lines: 1
-0x1303
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/ifalias
-Lines: 0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/ifindex
-Lines: 1
-2
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/iflink
-Lines: 1
-2
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/link_mode
-Lines: 1
-1
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/mtu
-Lines: 1
-1500
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/name_assign_type
-Lines: 1
-2
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/netdev_group
-Lines: 1
-0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/operstate
-Lines: 1
-up
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/phys_port_id
-Lines: 0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/phys_port_name
-Lines: 0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/phys_switch_id
-Lines: 0
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/speed
-Lines: 1
-1000
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/tx_queue_len
-Lines: 1
-1000
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-Path: fixtures/class/net/eth0/type
-Lines: 1
-1
-Mode: 644
-# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
\ No newline at end of file
diff --git a/sysfs/fs.go b/sysfs/fs.go
index fb15d43..c0cb9b6 100644
--- a/sysfs/fs.go
+++ b/sysfs/fs.go
@@ -14,95 +14,24 @@
 package sysfs
 
 import (
-	"fmt"
-	"os"
-	"path/filepath"
-
-	"github.com/prometheus/procfs/bcache"
-	"github.com/prometheus/procfs/xfs"
+	"github.com/prometheus/procfs/internal/fs"
 )
 
 // FS represents the pseudo-filesystem sys, which provides an interface to
 // kernel data structures.
-type FS string
+type FS struct {
+	sys fs.FS
+}
 
 // DefaultMountPoint is the common mount point of the sys filesystem.
-const DefaultMountPoint = "/sys"
+const DefaultMountPoint = fs.DefaultSysMountPoint
 
 // NewFS returns a new FS mounted under the given mountPoint. It will error
 // if the mount point can't be read.
 func NewFS(mountPoint string) (FS, error) {
-	info, err := os.Stat(mountPoint)
+	fs, err := fs.NewFS(mountPoint)
 	if err != nil {
-		return "", fmt.Errorf("could not read %s: %s", mountPoint, err)
+		return FS{}, err
 	}
-	if !info.IsDir() {
-		return "", fmt.Errorf("mount point %s is not a directory", mountPoint)
-	}
-
-	return FS(mountPoint), nil
-}
-
-// Path returns the path of the given subsystem relative to the sys root.
-func (fs FS) Path(p ...string) string {
-	return filepath.Join(append([]string{string(fs)}, p...)...)
-}
-
-// XFSStats retrieves XFS filesystem runtime statistics for each mounted XFS
-// filesystem.  Only available on kernel 4.4+.  On older kernels, an empty
-// slice of *xfs.Stats will be returned.
-func (fs FS) XFSStats() ([]*xfs.Stats, error) {
-	matches, err := filepath.Glob(fs.Path("fs/xfs/*/stats/stats"))
-	if err != nil {
-		return nil, err
-	}
-
-	stats := make([]*xfs.Stats, 0, len(matches))
-	for _, m := range matches {
-		f, err := os.Open(m)
-		if err != nil {
-			return nil, err
-		}
-
-		// "*" used in glob above indicates the name of the filesystem.
-		name := filepath.Base(filepath.Dir(filepath.Dir(m)))
-
-		// File must be closed after parsing, regardless of success or
-		// failure.  Defer is not used because of the loop.
-		s, err := xfs.ParseStats(f)
-		_ = f.Close()
-		if err != nil {
-			return nil, err
-		}
-
-		s.Name = name
-		stats = append(stats, s)
-	}
-
-	return stats, nil
-}
-
-// BcacheStats retrieves bcache runtime statistics for each bcache.
-func (fs FS) BcacheStats() ([]*bcache.Stats, error) {
-	matches, err := filepath.Glob(fs.Path("fs/bcache/*-*"))
-	if err != nil {
-		return nil, err
-	}
-
-	stats := make([]*bcache.Stats, 0, len(matches))
-	for _, uuidPath := range matches {
-		// "*-*" in glob above indicates the name of the bcache.
-		name := filepath.Base(uuidPath)
-
-		// stats
-		s, err := bcache.GetStats(uuidPath)
-		if err != nil {
-			return nil, err
-		}
-
-		s.Name = name
-		stats = append(stats, s)
-	}
-
-	return stats, nil
+	return FS{fs}, nil
 }
diff --git a/sysfs/net_class.go b/sysfs/net_class.go
index 46affd7..c28051e 100644
--- a/sysfs/net_class.go
+++ b/sysfs/net_class.go
@@ -11,48 +11,54 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build !windows
+
 package sysfs
 
 import (
 	"fmt"
 	"io/ioutil"
 	"os"
+	"path/filepath"
 	"reflect"
 	"strconv"
 	"strings"
-	"syscall"
+
+	"github.com/prometheus/procfs/internal/util"
 )
 
+const netclassPath = "class/net"
+
 // NetClassIface contains info from files in /sys/class/net/<iface>
 // for single interface (iface).
 type NetClassIface struct {
 	Name             string // Interface name
-	AddrAssignType   int64  `fileName:"addr_assign_type"`   // /sys/class/net/<iface>/addr_assign_type
-	AddrLen          int64  `fileName:"addr_len"`           // /sys/class/net/<iface>/addr_len
+	AddrAssignType   *int64 `fileName:"addr_assign_type"`   // /sys/class/net/<iface>/addr_assign_type
+	AddrLen          *int64 `fileName:"addr_len"`           // /sys/class/net/<iface>/addr_len
 	Address          string `fileName:"address"`            // /sys/class/net/<iface>/address
 	Broadcast        string `fileName:"broadcast"`          // /sys/class/net/<iface>/broadcast
-	Carrier          int64  `fileName:"carrier"`            // /sys/class/net/<iface>/carrier
-	CarrierChanges   int64  `fileName:"carrier_changes"`    // /sys/class/net/<iface>/carrier_changes
-	CarrierUpCount   int64  `fileName:"carrier_up_count"`   // /sys/class/net/<iface>/carrier_up_count
-	CarrierDownCount int64  `fileName:"carrier_down_count"` // /sys/class/net/<iface>/carrier_down_count
-	DevID            int64  `fileName:"dev_id"`             // /sys/class/net/<iface>/dev_id
-	Dormant          int64  `fileName:"dormant"`            // /sys/class/net/<iface>/dormant
+	Carrier          *int64 `fileName:"carrier"`            // /sys/class/net/<iface>/carrier
+	CarrierChanges   *int64 `fileName:"carrier_changes"`    // /sys/class/net/<iface>/carrier_changes
+	CarrierUpCount   *int64 `fileName:"carrier_up_count"`   // /sys/class/net/<iface>/carrier_up_count
+	CarrierDownCount *int64 `fileName:"carrier_down_count"` // /sys/class/net/<iface>/carrier_down_count
+	DevID            *int64 `fileName:"dev_id"`             // /sys/class/net/<iface>/dev_id
+	Dormant          *int64 `fileName:"dormant"`            // /sys/class/net/<iface>/dormant
 	Duplex           string `fileName:"duplex"`             // /sys/class/net/<iface>/duplex
-	Flags            int64  `fileName:"flags"`              // /sys/class/net/<iface>/flags
+	Flags            *int64 `fileName:"flags"`              // /sys/class/net/<iface>/flags
 	IfAlias          string `fileName:"ifalias"`            // /sys/class/net/<iface>/ifalias
-	IfIndex          int64  `fileName:"ifindex"`            // /sys/class/net/<iface>/ifindex
-	IfLink           int64  `fileName:"iflink"`             // /sys/class/net/<iface>/iflink
-	LinkMode         int64  `fileName:"link_mode"`          // /sys/class/net/<iface>/link_mode
-	MTU              int64  `fileName:"mtu"`                // /sys/class/net/<iface>/mtu
-	NameAssignType   int64  `fileName:"name_assign_type"`   // /sys/class/net/<iface>/name_assign_type
-	NetDevGroup      int64  `fileName:"netdev_group"`       // /sys/class/net/<iface>/netdev_group
+	IfIndex          *int64 `fileName:"ifindex"`            // /sys/class/net/<iface>/ifindex
+	IfLink           *int64 `fileName:"iflink"`             // /sys/class/net/<iface>/iflink
+	LinkMode         *int64 `fileName:"link_mode"`          // /sys/class/net/<iface>/link_mode
+	MTU              *int64 `fileName:"mtu"`                // /sys/class/net/<iface>/mtu
+	NameAssignType   *int64 `fileName:"name_assign_type"`   // /sys/class/net/<iface>/name_assign_type
+	NetDevGroup      *int64 `fileName:"netdev_group"`       // /sys/class/net/<iface>/netdev_group
 	OperState        string `fileName:"operstate"`          // /sys/class/net/<iface>/operstate
 	PhysPortID       string `fileName:"phys_port_id"`       // /sys/class/net/<iface>/phys_port_id
 	PhysPortName     string `fileName:"phys_port_name"`     // /sys/class/net/<iface>/phys_port_name
 	PhysSwitchID     string `fileName:"phys_switch_id"`     // /sys/class/net/<iface>/phys_switch_id
-	Speed            int64  `fileName:"speed"`              // /sys/class/net/<iface>/speed
-	TxQueueLen       int64  `fileName:"tx_queue_len"`       // /sys/class/net/<iface>/tx_queue_len
-	Type             int64  `fileName:"type"`               // /sys/class/net/<iface>/type
+	Speed            *int64 `fileName:"speed"`              // /sys/class/net/<iface>/speed
+	TxQueueLen       *int64 `fileName:"tx_queue_len"`       // /sys/class/net/<iface>/tx_queue_len
+	Type             *int64 `fileName:"type"`               // /sys/class/net/<iface>/type
 }
 
 // NetClass is collection of info for every interface (iface) in /sys/class/net. The map keys
@@ -69,23 +75,42 @@ func NewNetClass() (NetClass, error) {
 	return fs.NewNetClass()
 }
 
-// NewNetClass returns info for all net interfaces (iface) read from /sys/class/net/<iface>.
-func (fs FS) NewNetClass() (NetClass, error) {
-	path := fs.Path("class/net")
+// NetClassDevices scans /sys/class/net for devices and returns them as a list of names.
+func (fs FS) NetClassDevices() ([]string, error) {
+	var res []string
+	path := fs.sys.Path(netclassPath)
 
 	devices, err := ioutil.ReadDir(path)
 	if err != nil {
-		return NetClass{}, fmt.Errorf("cannot access %s dir %s", path, err)
+		return res, fmt.Errorf("cannot access %s dir %s", path, err)
+	}
+
+	for _, deviceDir := range devices {
+		if deviceDir.Mode().IsRegular() {
+			continue
+		}
+		res = append(res, deviceDir.Name())
 	}
 
+	return res, nil
+}
+
+// NewNetClass returns info for all net interfaces (iface) read from /sys/class/net/<iface>.
+func (fs FS) NewNetClass() (NetClass, error) {
+	devices, err := fs.NetClassDevices()
+	if err != nil {
+		return nil, err
+	}
+
+	path := fs.sys.Path(netclassPath)
 	netClass := NetClass{}
 	for _, deviceDir := range devices {
-		interfaceClass, err := netClass.parseNetClassIface(path + "/" + deviceDir.Name())
+		interfaceClass, err := netClass.parseNetClassIface(filepath.Join(path, deviceDir))
 		if err != nil {
 			return nil, err
 		}
-		interfaceClass.Name = deviceDir.Name()
-		netClass[deviceDir.Name()] = *interfaceClass
+		interfaceClass.Name = deviceDir
+		netClass[deviceDir] = *interfaceClass
 	}
 	return netClass, nil
 }
@@ -106,7 +131,7 @@ func (nc NetClass) parseNetClassIface(devicePath string) (*NetClassIface, error)
 			panic(fmt.Errorf("field %s does not have a filename tag", fieldType.Name))
 		}
 
-		fileContents, err := sysReadFile(devicePath + "/" + fieldType.Tag.Get("fileName"))
+		value, err := util.SysReadFile(devicePath + "/" + fieldType.Tag.Get("fileName"))
 
 		if err != nil {
 			if os.IsNotExist(err) || err.Error() == "operation not supported" || err.Error() == "invalid argument" {
@@ -114,25 +139,30 @@ func (nc NetClass) parseNetClassIface(devicePath string) (*NetClassIface, error)
 			}
 			return nil, fmt.Errorf("could not access file %s: %s", fieldType.Tag.Get("fileName"), err)
 		}
-		value := strings.TrimSpace(string(fileContents))
 
 		switch fieldValue.Kind() {
-		case reflect.Int64:
-			if strings.HasPrefix(value, "0x") {
-				intValue, err := strconv.ParseInt(value[2:], 16, 64)
-				if err != nil {
-					return nil, fmt.Errorf("expected hex value for %s, got: %s", fieldType.Name, value)
-				}
-				fieldValue.SetInt(intValue)
-			} else {
-				intValue, err := strconv.ParseInt(value, 10, 64)
-				if err != nil {
-					return nil, fmt.Errorf("expected Uint64 value for %s, got: %s", fieldType.Name, value)
-				}
-				fieldValue.SetInt(intValue)
-			}
 		case reflect.String:
 			fieldValue.SetString(value)
+		case reflect.Ptr:
+			var int64ptr *int64
+			switch fieldValue.Type() {
+			case reflect.TypeOf(int64ptr):
+				var intValue int64
+				if strings.HasPrefix(value, "0x") {
+					intValue, err = strconv.ParseInt(value[2:], 16, 64)
+					if err != nil {
+						return nil, fmt.Errorf("expected hex value for %s, got: %s", fieldType.Name, value)
+					}
+				} else {
+					intValue, err = strconv.ParseInt(value, 10, 64)
+					if err != nil {
+						return nil, fmt.Errorf("expected Uint64 value for %s, got: %s", fieldType.Name, value)
+					}
+				}
+				fieldValue.Set(reflect.ValueOf(&intValue))
+			default:
+				return nil, fmt.Errorf("unhandled pointer type %q", fieldValue.Type())
+			}
 		default:
 			return nil, fmt.Errorf("unhandled type %q", fieldValue.Kind())
 		}
@@ -140,26 +170,3 @@ func (nc NetClass) parseNetClassIface(devicePath string) (*NetClassIface, error)
 
 	return &interfaceClass, nil
 }
-
-// sysReadFile is a simplified ioutil.ReadFile that invokes syscall.Read directly.
-// https://github.com/prometheus/node_exporter/pull/728/files
-func sysReadFile(file string) ([]byte, error) {
-	f, err := os.Open(file)
-	if err != nil {
-		return nil, err
-	}
-	defer f.Close()
-
-	// On some machines, hwmon drivers are broken and return EAGAIN.  This causes
-	// Go's ioutil.ReadFile implementation to poll forever.
-	//
-	// Since we either want to read data or bail immediately, do the simplest
-	// possible read using syscall directly.
-	b := make([]byte, 128)
-	n, err := syscall.Read(int(f.Fd()), b)
-	if err != nil {
-		return nil, err
-	}
-
-	return b[:n], nil
-}
diff --git a/sysfs/system_cpu.go b/sysfs/system_cpu.go
new file mode 100644
index 0000000..4bcf86b
--- /dev/null
+++ b/sysfs/system_cpu.go
@@ -0,0 +1,156 @@
+// Copyright 2018 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build !windows
+
+package sysfs
+
+import (
+	"os"
+	"path/filepath"
+	"strings"
+
+	"golang.org/x/sync/errgroup"
+
+	"github.com/prometheus/procfs/internal/util"
+)
+
+// SystemCPUCpufreqStats contains stats from devices/system/cpu/cpu[0-9]*/cpufreq/...
+type SystemCPUCpufreqStats struct {
+	Name                     string
+	CpuinfoCurrentFrequency  *uint64
+	CpuinfoMinimumFrequency  *uint64
+	CpuinfoMaximumFrequency  *uint64
+	CpuinfoTransitionLatency *uint64
+	ScalingCurrentFrequency  *uint64
+	ScalingMinimumFrequency  *uint64
+	ScalingMaximumFrequency  *uint64
+	AvailableGovernors       string
+	Driver                   string
+	Governor                 string
+	RelatedCpus              string
+	SetSpeed                 string
+}
+
+// TODO: Add topology support.
+
+// TODO: Add thermal_throttle support.
+
+// NewSystemCpufreq returns CPU frequency metrics for all CPUs.
+func NewSystemCpufreq() ([]SystemCPUCpufreqStats, error) {
+	fs, err := NewFS(DefaultMountPoint)
+	if err != nil {
+		return []SystemCPUCpufreqStats{}, err
+	}
+
+	return fs.NewSystemCpufreq()
+}
+
+// NewSystemCpufreq returns CPU frequency metrics for all CPUs.
+func (fs FS) NewSystemCpufreq() ([]SystemCPUCpufreqStats, error) {
+	var g errgroup.Group
+
+	cpus, err := filepath.Glob(fs.sys.Path("devices/system/cpu/cpu[0-9]*"))
+	if err != nil {
+		return nil, err
+	}
+
+	systemCpufreq := make([]SystemCPUCpufreqStats, len(cpus))
+	for i, cpu := range cpus {
+		cpuName := strings.TrimPrefix(filepath.Base(cpu), "cpu")
+
+		cpuCpufreqPath := filepath.Join(cpu, "cpufreq")
+		_, err = os.Stat(cpuCpufreqPath)
+		if os.IsNotExist(err) {
+			continue
+		} else if err != nil {
+			return nil, err
+		}
+
+		// Execute the parsing of each CPU in parallel.
+		// This is done because the kernel intentionally delays access to each CPU by
+		// 50 milliseconds to avoid DDoSing possibly expensive functions.
+		i := i // https://golang.org/doc/faq#closures_and_goroutines
+		g.Go(func() error {
+			cpufreq, err := parseCpufreqCpuinfo(cpuCpufreqPath)
+			if err == nil {
+				cpufreq.Name = cpuName
+				systemCpufreq[i] = *cpufreq
+			}
+			return err
+		})
+	}
+
+	if err = g.Wait(); err != nil {
+		return nil, err
+	}
+
+	return systemCpufreq, nil
+}
+
+func parseCpufreqCpuinfo(cpuPath string) (*SystemCPUCpufreqStats, error) {
+	uintFiles := []string{
+		"cpuinfo_cur_freq",
+		"cpuinfo_max_freq",
+		"cpuinfo_min_freq",
+		"cpuinfo_transition_latency",
+		"scaling_cur_freq",
+		"scaling_max_freq",
+		"scaling_min_freq",
+	}
+	uintOut := make([]*uint64, len(uintFiles))
+
+	for i, f := range uintFiles {
+		v, err := util.ReadUintFromFile(filepath.Join(cpuPath, f))
+		if err != nil {
+			if os.IsNotExist(err) || os.IsPermission(err) {
+				continue
+			}
+			return &SystemCPUCpufreqStats{}, err
+		}
+
+		uintOut[i] = &v
+	}
+
+	stringFiles := []string{
+		"scaling_available_governors",
+		"scaling_driver",
+		"scaling_governor",
+		"related_cpus",
+		"scaling_setspeed",
+	}
+	stringOut := make([]string, len(stringFiles))
+	var err error
+
+	for i, f := range stringFiles {
+		stringOut[i], err = util.SysReadFile(filepath.Join(cpuPath, f))
+		if err != nil {
+			return &SystemCPUCpufreqStats{}, err
+		}
+	}
+
+	return &SystemCPUCpufreqStats{
+		CpuinfoCurrentFrequency:  uintOut[0],
+		CpuinfoMaximumFrequency:  uintOut[1],
+		CpuinfoMinimumFrequency:  uintOut[2],
+		CpuinfoTransitionLatency: uintOut[3],
+		ScalingCurrentFrequency:  uintOut[4],
+		ScalingMaximumFrequency:  uintOut[5],
+		ScalingMinimumFrequency:  uintOut[6],
+		AvailableGovernors:       stringOut[0],
+		Driver:                   stringOut[1],
+		Governor:                 stringOut[2],
+		RelatedCpus:              stringOut[3],
+		SetSpeed:                 stringOut[4],
+	}, nil
+}
diff --git a/ttar b/ttar
index b0171a1..19ef02b 100755
--- a/ttar
+++ b/ttar
@@ -86,8 +86,10 @@ Usage:   $bname [-C <DIR>] -c -f <ARCHIVE> <FILE...> (create archive)
          $bname [-C <DIR>] -x -f <ARCHIVE>           (extract archive)
 
 Options:
-         -C <DIR>                                    (change directory)
-         -v                                          (verbose)
+         -C <DIR>           (change directory)
+         -v                 (verbose)
+         --recursive-unlink (recursively delete existing directory if path
+                             collides with file or directory to extract)
 
 Example: Change to sysfs directory, create ttar file from fixtures directory
          $bname -C sysfs -c -f sysfs/fixtures.ttar fixtures/
@@ -111,8 +113,9 @@ function set_cmd {
 }
 
 unset VERBOSE
+unset RECURSIVE_UNLINK
 
-while getopts :cf:htxvC: opt; do
+while getopts :cf:-:htxvC: opt; do
     case $opt in
         c)
             set_cmd "create"
@@ -136,6 +139,18 @@ while getopts :cf:htxvC: opt; do
         C)
             CDIR=$OPTARG
             ;;
+        -)
+            case $OPTARG in
+                recursive-unlink)
+                    RECURSIVE_UNLINK="yes"
+                    ;;
+                *)
+                    echo -e "Error: invalid option -$OPTARG"
+                    echo
+                    usage 1
+                    ;;
+            esac
+            ;;
         *)
             echo >&2 "ERROR: invalid option -$OPTARG"
             echo
@@ -212,16 +227,16 @@ function extract {
         local eof_without_newline
         if [ "$size" -gt 0 ]; then
             if [[ "$line" =~ [^\\]EOF ]]; then
-                # An EOF not preceeded by a backslash indicates that the line
+                # An EOF not preceded by a backslash indicates that the line
                 # does not end with a newline
                 eof_without_newline=1
             else
                 eof_without_newline=0
             fi
             # Replace NULLBYTE with null byte if at beginning of line
-            # Replace NULLBYTE with null byte unless preceeded by backslash
+            # Replace NULLBYTE with null byte unless preceded by backslash
             # Remove one backslash in front of NULLBYTE (if any)
-            # Remove EOF unless preceeded by backslash
+            # Remove EOF unless preceded by backslash
             # Remove one backslash in front of EOF
             if [ $USE_PYTHON -eq 1 ]; then
                 echo -n "$line" | python -c "$PYTHON_EXTRACT_FILTER" >> "$path"
@@ -245,7 +260,16 @@ function extract {
         fi
         if [[ $line =~ ^Path:\ (.*)$ ]]; then
             path=${BASH_REMATCH[1]}
-            if [ -e "$path" ] || [ -L "$path" ]; then
+            if [ -L "$path" ]; then
+                rm "$path"
+            elif [ -d "$path" ]; then
+                if [ "${RECURSIVE_UNLINK:-}" == "yes" ]; then
+                    rm -r "$path"
+                else
+                    # Safe because symlinks to directories are dealt with above
+                    rmdir "$path"
+                fi
+            elif [ -e "$path" ]; then
                 rm "$path"
             fi
         elif [[ $line =~ ^Lines:\ (.*)$ ]]; then
@@ -338,8 +362,8 @@ function _create {
             else
                 < "$file" \
                     sed 's/EOF/\\EOF/g;
-                         s/NULLBYTE/\\NULLBYTE/g;
-                         s/\x0/NULLBYTE/g;
+                            s/NULLBYTE/\\NULLBYTE/g;
+                            s/\x0/NULLBYTE/g;
                     '
             fi
             if [[ "$eof_without_newline" -eq 1 ]]; then
diff --git a/xfrm.go b/xfrm.go
index ffe9df5..30aa417 100644
--- a/xfrm.go
+++ b/xfrm.go
@@ -97,7 +97,7 @@ func NewXfrmStat() (XfrmStat, error) {
 
 // NewXfrmStat reads the xfrm_stat statistics from the 'proc' filesystem.
 func (fs FS) NewXfrmStat() (XfrmStat, error) {
-	file, err := os.Open(fs.Path("net/xfrm_stat"))
+	file, err := os.Open(fs.proc.Path("net/xfrm_stat"))
 	if err != nil {
 		return XfrmStat{}, err
 	}
@@ -113,7 +113,7 @@ func (fs FS) NewXfrmStat() (XfrmStat, error) {
 
 		if len(fields) != 2 {
 			return XfrmStat{}, fmt.Errorf(
-				"couldnt parse %s line %s", file.Name(), s.Text())
+				"couldn't parse %s line %s", file.Name(), s.Text())
 		}
 
 		name := fields[0]
diff --git a/xfs/parse.go b/xfs/parse.go
index 2bc0ef3..b3d8634 100644
--- a/xfs/parse.go
+++ b/xfs/parse.go
@@ -43,15 +43,15 @@ func ParseStats(r io.Reader) (*Stats, error) {
 		fieldXpc         = "xpc"
 
 		// Unimplemented at this time due to lack of documentation.
-		fieldPushAil = "push_ail"
-		fieldXstrat  = "xstrat"
-		fieldAbtb2   = "abtb2"
-		fieldAbtc2   = "abtc2"
-		fieldBmbt2   = "bmbt2"
-		fieldIbt2    = "ibt2"
-		fieldFibt2   = "fibt2"
-		fieldQm      = "qm"
-		fieldDebug   = "debug"
+		// fieldPushAil = "push_ail"
+		// fieldXstrat  = "xstrat"
+		// fieldAbtb2   = "abtb2"
+		// fieldAbtc2   = "abtc2"
+		// fieldBmbt2   = "bmbt2"
+		// fieldIbt2    = "ibt2"
+		// fieldFibt2   = "fibt2"
+		// fieldQm      = "qm"
+		// fieldDebug   = "debug"
 	)
 
 	var xfss Stats
diff --git a/xfs/xfs.go b/xfs/xfs.go
index d86794b..8a7288f 100644
--- a/xfs/xfs.go
+++ b/xfs/xfs.go
@@ -14,6 +14,14 @@
 // Package xfs provides access to statistics exposed by the XFS filesystem.
 package xfs
 
+import (
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/prometheus/procfs/internal/fs"
+)
+
 // Stats contains XFS filesystem runtime statistics, parsed from
 // /proc/fs/xfs/stat.
 //
@@ -161,3 +169,76 @@ type ExtendedPrecisionStats struct {
 	WriteBytes uint64
 	ReadBytes  uint64
 }
+
+// FS represents the pseudo-filesystems proc and sys, which provides an interface to
+// kernel data structures.
+type FS struct {
+	proc *fs.FS
+	sys  *fs.FS
+}
+
+// NewFS returns a new XFS handle using the given proc and sys mountPoints. It will error
+// if either of the mounts point can't be read.
+func NewFS(procMountPoint string, sysMountPoint string) (FS, error) {
+	if strings.TrimSpace(procMountPoint) == "" {
+		procMountPoint = fs.DefaultProcMountPoint
+	}
+	procfs, err := fs.NewFS(procMountPoint)
+	if err != nil {
+		return FS{}, err
+	}
+	if strings.TrimSpace(sysMountPoint) == "" {
+		sysMountPoint = fs.DefaultSysMountPoint
+	}
+	sysfs, err := fs.NewFS(sysMountPoint)
+	if err != nil {
+		return FS{}, err
+	}
+	return FS{&procfs, &sysfs}, nil
+}
+
+// ProcStat retrieves XFS filesystem runtime statistics
+// from proc/fs/xfs/stat given the profs mount point.
+func (fs FS) ProcStat() (*Stats, error) {
+	f, err := os.Open(fs.proc.Path("fs/xfs/stat"))
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+
+	return ParseStats(f)
+}
+
+// SysStats retrieves XFS filesystem runtime statistics for each mounted XFS
+// filesystem.  Only available on kernel 4.4+.  On older kernels, an empty
+// slice of *xfs.Stats will be returned.
+func (fs FS) SysStats() ([]*Stats, error) {
+	matches, err := filepath.Glob(fs.sys.Path("fs/xfs/*/stats/stats"))
+	if err != nil {
+		return nil, err
+	}
+
+	stats := make([]*Stats, 0, len(matches))
+	for _, m := range matches {
+		f, err := os.Open(m)
+		if err != nil {
+			return nil, err
+		}
+
+		// "*" used in glob above indicates the name of the filesystem.
+		name := filepath.Base(filepath.Dir(filepath.Dir(m)))
+
+		// File must be closed after parsing, regardless of success or
+		// failure.  Defer is not used because of the loop.
+		s, err := ParseStats(f)
+		_ = f.Close()
+		if err != nil {
+			return nil, err
+		}
+
+		s.Name = name
+		stats = append(stats, s)
+	}
+
+	return stats, nil
+}


Test patch:
diff --git a/bcache/get_test.go b/bcache/get_test.go
index 1d41a5a..e448797 100644
--- a/bcache/get_test.go
+++ b/bcache/get_test.go
@@ -18,6 +18,52 @@ import (
 	"testing"
 )
 
+func TestFSBcacheStats(t *testing.T) {
+	bcache, err := NewFS("../fixtures/sys")
+	if err != nil {
+		t.Fatalf("failed to access bcache fs: %v", err)
+	}
+	stats, err := bcache.Stats()
+	if err != nil {
+		t.Fatalf("failed to parse bcache stats: %v", err)
+	}
+
+	tests := []struct {
+		name   string
+		bdevs  int
+		caches int
+	}{
+		{
+			name:   "deaddd54-c735-46d5-868e-f331c5fd7c74",
+			bdevs:  1,
+			caches: 1,
+		},
+	}
+
+	const expect = 1
+
+	if l := len(stats); l != expect {
+		t.Fatalf("unexpected number of bcache stats: %d", l)
+	}
+	if l := len(tests); l != expect {
+		t.Fatalf("unexpected number of tests: %d", l)
+	}
+
+	for i, tt := range tests {
+		if want, got := tt.name, stats[i].Name; want != got {
+			t.Errorf("unexpected stats name:\nwant: %q\nhave: %q", want, got)
+		}
+
+		if want, got := tt.bdevs, len(stats[i].Bdevs); want != got {
+			t.Errorf("unexpected value allocated:\nwant: %d\nhave: %d", want, got)
+		}
+
+		if want, got := tt.caches, len(stats[i].Caches); want != got {
+			t.Errorf("unexpected value allocated:\nwant: %d\nhave: %d", want, got)
+		}
+	}
+}
+
 func TestDehumanizeTests(t *testing.T) {
 	dehumanizeTests := []struct {
 		in      []byte
diff --git a/blockdevice/stats_test.go b/blockdevice/stats_test.go
new file mode 100644
index 0000000..2fea2c8
--- /dev/null
+++ b/blockdevice/stats_test.go
@@ -0,0 +1,98 @@
+// Copyright 2018 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package blockdevice
+
+import (
+	"testing"
+)
+
+const (
+	failMsgFormat  = "%v, expected %v, actual %v"
+	procfsFixtures = "../fixtures/proc"
+	sysfsFixtures  = "../fixtures/sys"
+)
+
+func TestDiskstats(t *testing.T) {
+	blockdevice, err := NewFS(procfsFixtures, sysfsFixtures)
+	if err != nil {
+		t.Fatalf("failed to access blockdevice fs: %v", err)
+	}
+	diskstats, err := blockdevice.ProcDiskstats()
+	if err != nil {
+		t.Fatal(err)
+	}
+	expectedNumOfDevices := 49
+	if len(diskstats) != expectedNumOfDevices {
+		t.Errorf(failMsgFormat, "Incorrect number of devices", expectedNumOfDevices, len(diskstats))
+	}
+	if diskstats[0].DeviceName != "ram0" {
+		t.Errorf(failMsgFormat, "Incorrect device name", "ram0", diskstats[0].DeviceName)
+	}
+	if diskstats[1].IoStatsCount != 14 {
+		t.Errorf(failMsgFormat, "Incorrect number of stats read", 14, diskstats[0].IoStatsCount)
+	}
+	if diskstats[24].WriteIOs != 28444756 {
+		t.Errorf(failMsgFormat, "Incorrect writes completed", 28444756, diskstats[24].WriteIOs)
+	}
+	if diskstats[48].DiscardTicks != 11130 {
+		t.Errorf(failMsgFormat, "Incorrect discard time", 11130, diskstats[48].DiscardTicks)
+	}
+	if diskstats[48].IoStatsCount != 18 {
+		t.Errorf(failMsgFormat, "Incorrect number of stats read", 18, diskstats[48].IoStatsCount)
+	}
+}
+
+func TestBlockDevice(t *testing.T) {
+	blockdevice, err := NewFS("../fixtures/proc", "../fixtures/sys")
+	if err != nil {
+		t.Fatalf("failed to access blockdevice fs: %v", err)
+	}
+	devices, err := blockdevice.SysBlockDevices()
+	if err != nil {
+		t.Fatal(err)
+	}
+	expectedNumOfDevices := 2
+	if len(devices) != expectedNumOfDevices {
+		t.Fatalf(failMsgFormat, "Incorrect number of devices", expectedNumOfDevices, len(devices))
+	}
+	if devices[0] != "dm-0" {
+		t.Errorf(failMsgFormat, "Incorrect device name", "dm-0", devices[0])
+	}
+	device0stats, count, err := blockdevice.SysBlockDeviceStat(devices[0])
+	if err != nil {
+		t.Fatal(err)
+	}
+	if count != 11 {
+		t.Errorf(failMsgFormat, "Incorrect number of stats read", 11, count)
+	}
+	if device0stats.ReadIOs != 6447303 {
+		t.Errorf(failMsgFormat, "Incorrect read I/Os", 6447303, device0stats.ReadIOs)
+	}
+	if device0stats.WeightedIOTicks != 6088971 {
+		t.Errorf(failMsgFormat, "Incorrect time in queue", 6088971, device0stats.WeightedIOTicks)
+	}
+	device1stats, count, err := blockdevice.SysBlockDeviceStat(devices[1])
+	if count != 15 {
+		t.Errorf(failMsgFormat, "Incorrect number of stats read", 15, count)
+	}
+	if err != nil {
+		t.Fatal(err)
+	}
+	if device1stats.WriteSectors != 286915323 {
+		t.Errorf(failMsgFormat, "Incorrect write merges", 286915323, device1stats.WriteSectors)
+	}
+	if device1stats.DiscardTicks != 12 {
+		t.Errorf(failMsgFormat, "Incorrect discard ticks", 12, device1stats.DiscardTicks)
+	}
+}
diff --git a/buddyinfo_test.go b/buddyinfo_test.go
index bcf9355..7016466 100644
--- a/buddyinfo_test.go
+++ b/buddyinfo_test.go
@@ -19,7 +19,7 @@ import (
 )
 
 func TestBuddyInfo(t *testing.T) {
-	buddyInfo, err := FS("fixtures/buddyinfo/valid").NewBuddyInfo()
+	buddyInfo, err := getProcFixtures(t).NewBuddyInfo()
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -41,24 +41,34 @@ func TestBuddyInfo(t *testing.T) {
 	}
 }
 
-func TestBuddyInfoShort(t *testing.T) {
-	_, err := FS("fixtures/buddyinfo/short").NewBuddyInfo()
+func TestParseBuddyInfoShort(t *testing.T) {
+
+	testdata := `Node 0, zone
+Node 0, zone
+Node 0, zone
+`
+	reader := strings.NewReader(testdata)
+	_, err := parseBuddyInfo(reader)
 	if err == nil {
-		t.Errorf("expected error, but none occurred")
+		t.Fatalf("expected error, but none occurred")
 	}
-
 	if want, got := "invalid number of fields when parsing buddyinfo", err.Error(); want != got {
-		t.Errorf("wrong error returned, wanted %q, got %q", want, got)
+		t.Fatalf("wrong error returned, wanted %q, got %q", want, got)
 	}
 }
 
-func TestBuddyInfoSizeMismatch(t *testing.T) {
-	_, err := FS("fixtures/buddyinfo/sizemismatch").NewBuddyInfo()
+func TestParseBuddyInfoSizeMismatch(t *testing.T) {
+
+	testdata := `Node 0, zone      DMA      1      0      1      0      2      1      1      0      1      1      3
+Node 0, zone    DMA32    759    572    791    475    194     45     12      0      0      0      0      0
+Node 0, zone   Normal   4381   1093    185   1530    567    102      4      0      0      0
+`
+	reader := strings.NewReader(testdata)
+	_, err := parseBuddyInfo(reader)
 	if err == nil {
-		t.Errorf("expected error, but none occurred")
+		t.Fatalf("expected error, but none occurred")
 	}
-
 	if want, got := "mismatch in number of buddyinfo buckets", err.Error(); !strings.HasPrefix(got, want) {
-		t.Errorf("wrong error returned, wanted prefix %q, got %q", want, got)
+		t.Fatalf("wrong error returned, wanted prefix %q, got %q", want, got)
 	}
 }
diff --git a/fs_test.go b/fs_test.go
index a4e07f5..6e57745 100644
--- a/fs_test.go
+++ b/fs_test.go
@@ -15,6 +15,10 @@ package procfs
 
 import "testing"
 
+const (
+	procTestFixtures = "fixtures/proc"
+)
+
 func TestNewFS(t *testing.T) {
 	if _, err := NewFS("foobar"); err == nil {
 		t.Error("want NewFS to fail for non-existing mount point")
@@ -23,17 +27,13 @@ func TestNewFS(t *testing.T) {
 	if _, err := NewFS("procfs.go"); err == nil {
 		t.Error("want NewFS to fail if mount point is not a directory")
 	}
+	getProcFixtures(t)
 }
 
-func TestFSXFSStats(t *testing.T) {
-	stats, err := FS("fixtures").XFSStats()
+func getProcFixtures(t *testing.T) FS {
+	fs, err := NewFS(procTestFixtures)
 	if err != nil {
-		t.Fatalf("failed to parse XFS stats: %v", err)
-	}
-
-	// Very lightweight test just to sanity check the path used
-	// to open XFS stats. Heavier tests in package xfs.
-	if want, got := uint32(92447), stats.ExtentAllocation.ExtentsAllocated; want != got {
-		t.Errorf("unexpected extents allocated:\nwant: %d\nhave: %d", want, got)
+		t.Fatal(err)
 	}
+	return fs
 }
diff --git a/internal/fs/fs_test.go b/internal/fs/fs_test.go
new file mode 100644
index 0000000..bebcbe2
--- /dev/null
+++ b/internal/fs/fs_test.go
@@ -0,0 +1,34 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fs
+
+import "testing"
+
+const (
+	sysTestFixtures = "../../fixtures/sys"
+)
+
+func TestNewFS(t *testing.T) {
+	if _, err := NewFS("foobar"); err == nil {
+		t.Error("want NewFS to fail for non-existing mount point")
+	}
+
+	if _, err := NewFS("doc.go"); err == nil {
+		t.Error("want NewFS to fail if mount point is not a directory")
+	}
+
+	if _, err := NewFS(sysTestFixtures); err != nil {
+		t.Error("want NewFS to succeed if mount point exists")
+	}
+}
diff --git a/ipvs_test.go b/ipvs_test.go
index 9c34e6d..aa87d3c 100644
--- a/ipvs_test.go
+++ b/ipvs_test.go
@@ -159,7 +159,11 @@ var (
 )
 
 func TestIPVSStats(t *testing.T) {
-	stats, err := FS("fixtures").NewIPVSStats()
+	fs, err := NewFS(procTestFixtures)
+	if err != nil {
+		t.Fatal(err)
+	}
+	stats, err := fs.NewIPVSStats()
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -213,7 +217,7 @@ func TestParseIPPortIPv6(t *testing.T) {
 }
 
 func TestIPVSBackendStatus(t *testing.T) {
-	backendStats, err := FS("fixtures").NewIPVSBackendStatus()
+	backendStats, err := getProcFixtures(t).NewIPVSBackendStatus()
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/mdstat_test.go b/mdstat_test.go
index 8819228..cc1b557 100644
--- a/mdstat_test.go
+++ b/mdstat_test.go
@@ -18,7 +18,7 @@ import (
 )
 
 func TestMDStat(t *testing.T) {
-	mdStates, err := FS("fixtures").ParseMDStat()
+	mdStates, err := getProcFixtures(t).ParseMDStat()
 	if err != nil {
 		t.Fatalf("parsing of reference-file failed entirely: %s", err)
 	}
diff --git a/mountstats_test.go b/mountstats_test.go
index 7df1d15..727af07 100644
--- a/mountstats_test.go
+++ b/mountstats_test.go
@@ -103,7 +103,12 @@ func TestMountStats(t *testing.T) {
 			invalid: true,
 		},
 		{
-			name: "NFSv3 device with transport stats version 1.0 OK",
+			name:    "NFSv3 device with bad transport protocol",
+			s:       "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs4 statvers=1.1\nxprt: tcpx 0 0 0 0 0 0 0 0 0 0",
+			invalid: true,
+		},
+		{
+			name: "NFSv3 device using TCP with transport stats version 1.0 OK",
 			s:    "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs statvers=1.0\nxprt: tcp 1 2 3 4 5 6 7 8 9 10",
 			mounts: []*Mount{{
 				Device: "192.168.1.1:/srv",
@@ -112,6 +117,63 @@ func TestMountStats(t *testing.T) {
 				Stats: &MountStatsNFS{
 					StatVersion: "1.0",
 					Transport: NFSTransportStats{
+						Protocol:                 "tcp",
+						Port:                     1,
+						Bind:                     2,
+						Connect:                  3,
+						ConnectIdleTime:          4,
+						IdleTime:                 5 * time.Second,
+						Sends:                    6,
+						Receives:                 7,
+						BadTransactionIDs:        8,
+						CumulativeActiveRequests: 9,
+						CumulativeBacklog:        10,
+						MaximumRPCSlotsUsed:      0, // these three are not
+						CumulativeSendingQueue:   0, // present in statvers=1.0
+						CumulativePendingQueue:   0, //
+					},
+				},
+			}},
+		},
+		{
+			name: "NFSv3 device using UDP with transport stats version 1.0 OK",
+			s:    "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs statvers=1.0\nxprt: udp 1 2 3 4 5 6 7",
+			mounts: []*Mount{{
+				Device: "192.168.1.1:/srv",
+				Mount:  "/mnt/nfs",
+				Type:   "nfs",
+				Stats: &MountStatsNFS{
+					StatVersion: "1.0",
+					Transport: NFSTransportStats{
+						Protocol:                 "udp",
+						Port:                     1,
+						Bind:                     2,
+						Connect:                  0,
+						ConnectIdleTime:          0,
+						IdleTime:                 0,
+						Sends:                    3,
+						Receives:                 4,
+						BadTransactionIDs:        5,
+						CumulativeActiveRequests: 6,
+						CumulativeBacklog:        7,
+						MaximumRPCSlotsUsed:      0, // these three are not
+						CumulativeSendingQueue:   0, // present in statvers=1.0
+						CumulativePendingQueue:   0, //
+					},
+				},
+			}},
+		},
+		{
+			name: "NFSv3 device using TCP with transport stats version 1.1 OK",
+			s:    "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs statvers=1.1\nxprt: tcp 1 2 3 4 5 6 7 8 9 10 11 12 13",
+			mounts: []*Mount{{
+				Device: "192.168.1.1:/srv",
+				Mount:  "/mnt/nfs",
+				Type:   "nfs",
+				Stats: &MountStatsNFS{
+					StatVersion: "1.1",
+					Transport: NFSTransportStats{
+						Protocol:                 "tcp",
 						Port:                     1,
 						Bind:                     2,
 						Connect:                  3,
@@ -122,10 +184,54 @@ func TestMountStats(t *testing.T) {
 						BadTransactionIDs:        8,
 						CumulativeActiveRequests: 9,
 						CumulativeBacklog:        10,
+						MaximumRPCSlotsUsed:      11,
+						CumulativeSendingQueue:   12,
+						CumulativePendingQueue:   13,
 					},
 				},
 			}},
 		},
+		{
+			name: "NFSv3 device using UDP with transport stats version 1.1 OK",
+			s:    "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs statvers=1.1\nxprt: udp 1 2 3 4 5 6 7 8 9 10",
+			mounts: []*Mount{{
+				Device: "192.168.1.1:/srv",
+				Mount:  "/mnt/nfs",
+				Type:   "nfs",
+				Stats: &MountStatsNFS{
+					StatVersion: "1.1",
+					Transport: NFSTransportStats{
+						Protocol:                 "udp",
+						Port:                     1,
+						Bind:                     2,
+						Connect:                  0, // these three are not
+						ConnectIdleTime:          0, // present for UDP
+						IdleTime:                 0, //
+						Sends:                    3,
+						Receives:                 4,
+						BadTransactionIDs:        5,
+						CumulativeActiveRequests: 6,
+						CumulativeBacklog:        7,
+						MaximumRPCSlotsUsed:      8,
+						CumulativeSendingQueue:   9,
+						CumulativePendingQueue:   10,
+					},
+				},
+			}},
+		},
+		{
+			name: "NFSv3 device with mountaddr OK",
+			s:    "device 192.168.1.1:/srv mounted on /mnt/nfs with fstype nfs statvers=1.1\nopts: rw,vers=3,mountaddr=192.168.1.1,proto=udp\n",
+			mounts: []*Mount{{
+				Device: "192.168.1.1:/srv",
+				Mount:  "/mnt/nfs",
+				Type:   "nfs",
+				Stats: &MountStatsNFS{
+					StatVersion:  "1.1",
+					MountAddress: "192.168.1.1",
+				},
+			}},
+		},
 		{
 			name: "device rootfs OK",
 			s:    `device rootfs mounted on / with fstype rootfs`,
@@ -148,7 +254,7 @@ func TestMountStats(t *testing.T) {
 			}},
 		},
 		{
-			name: "fixtures OK",
+			name: "fixtures/proc OK",
 			mounts: []*Mount{
 				{
 					Device: "rootfs",
@@ -212,6 +318,7 @@ func TestMountStats(t *testing.T) {
 							},
 						},
 						Transport: NFSTransportStats{
+							Protocol:                 "tcp",
 							Port:                     832,
 							Connect:                  1,
 							IdleTime:                 11 * time.Second,
@@ -237,7 +344,7 @@ func TestMountStats(t *testing.T) {
 		if tt.s != "" {
 			mounts, err = parseMountStats(strings.NewReader(tt.s))
 		} else {
-			proc, e := FS("fixtures").NewProc(26231)
+			proc, e := getProcFixtures(t).NewProc(26231)
 			if e != nil {
 				t.Fatalf("failed to create proc: %v", err)
 			}
@@ -269,6 +376,7 @@ func mountsStr(mounts []*Mount) string {
 			continue
 		}
 
+		out += fmt.Sprintf("\n\t- mountaddr: %s", stats.MountAddress)
 		out += fmt.Sprintf("\n\t- v%s, age: %s", stats.StatVersion, stats.Age)
 		out += fmt.Sprintf("\n\t- bytes: %v", stats.Bytes)
 		out += fmt.Sprintf("\n\t- events: %v", stats.Events)
diff --git a/net_dev_test.go b/net_dev_test.go
index b162e9c..1c829bb 100644
--- a/net_dev_test.go
+++ b/net_dev_test.go
@@ -32,12 +32,12 @@ func TestNetDevParseLine(t *testing.T) {
 }
 
 func TestNewNetDev(t *testing.T) {
-	fs, err := NewFS("fixtures")
+	fs, err := NewFS(procTestFixtures)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	nd, err := fs.NewNetDev()
+	netDev, err := fs.NewNetDev()
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -49,10 +49,10 @@ func TestNewNetDev(t *testing.T) {
 		"eth0":        {Name: "eth0", RxBytes: 874354587, RxPackets: 1036395, TxBytes: 563352563, TxPackets: 732147},
 	}
 
-	if want, have := len(lines), len(nd); want != have {
+	if want, have := len(lines), len(netDev); want != have {
 		t.Errorf("want %d parsed net/dev lines, have %d", want, have)
 	}
-	for _, line := range nd {
+	for _, line := range netDev {
 		if want, have := lines[line.Name], line; want != have {
 			t.Errorf("%s: want %v, have %v", line.Name, want, have)
 		}
@@ -60,12 +60,12 @@ func TestNewNetDev(t *testing.T) {
 }
 
 func TestProcNewNetDev(t *testing.T) {
-	p, err := FS("fixtures").NewProc(26231)
+	p, err := getProcFixtures(t).NewProc(26231)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	nd, err := p.NewNetDev()
+	netDev, err := p.NewNetDev()
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -75,10 +75,10 @@ func TestProcNewNetDev(t *testing.T) {
 		"eth0": {Name: "eth0", RxBytes: 438, RxPackets: 5, TxBytes: 648, TxPackets: 8},
 	}
 
-	if want, have := len(lines), len(nd); want != have {
+	if want, have := len(lines), len(netDev); want != have {
 		t.Errorf("want %d parsed net/dev lines, have %d", want, have)
 	}
-	for _, line := range nd {
+	for _, line := range netDev {
 		if want, have := lines[line.Name], line; want != have {
 			t.Errorf("%s: want %v, have %v", line.Name, want, have)
 		}
diff --git a/proc_io_test.go b/proc_io_test.go
index 1afdbd4..c0d55cd 100644
--- a/proc_io_test.go
+++ b/proc_io_test.go
@@ -16,7 +16,7 @@ package procfs
 import "testing"
 
 func TestProcIO(t *testing.T) {
-	p, err := FS("fixtures").NewProc(26231)
+	p, err := getProcFixtures(t).NewProc(26231)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/proc_limits_test.go b/proc_limits_test.go
index ebb43ae..290f675 100644
--- a/proc_limits_test.go
+++ b/proc_limits_test.go
@@ -16,7 +16,7 @@ package procfs
 import "testing"
 
 func TestNewLimits(t *testing.T) {
-	p, err := FS("fixtures").NewProc(26231)
+	p, err := getProcFixtures(t).NewProc(26231)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/proc_ns_test.go b/proc_ns_test.go
index abfd63e..ef62266 100644
--- a/proc_ns_test.go
+++ b/proc_ns_test.go
@@ -18,7 +18,7 @@ import (
 )
 
 func TestNewNamespaces(t *testing.T) {
-	p, err := FS("fixtures").NewProc(26231)
+	p, err := getProcFixtures(t).NewProc(26231)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/proc_psi_test.go b/proc_psi_test.go
new file mode 100644
index 0000000..d238fe1
--- /dev/null
+++ b/proc_psi_test.go
@@ -0,0 +1,149 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"strings"
+	"testing"
+)
+
+func TestPSIStats(t *testing.T) {
+	t.Run("fake", func(*testing.T) {
+		stats, err := getProcFixtures(t).NewPSIStatsForResource("fake")
+		if err == nil {
+			t.Fatal("fake resource does not have PSI statistics")
+		}
+
+		if stats.Some != nil || stats.Full != nil {
+			t.Error("a fake resource cannot have PSILine entries")
+		}
+	})
+
+	t.Run("cpu", func(t *testing.T) {
+		stats, err := getProcFixtures(t).NewPSIStatsForResource("cpu")
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		if stats.Full != nil {
+			t.Fatal("cpu resource cannot have 'full' stats")
+		}
+
+		if stats.Some == nil {
+			t.Fatal("cpu resource should not have nil 'some' stats")
+		}
+
+		testCases := []struct {
+			name string
+			got  float64
+			want float64
+		}{
+			{"Avg10", stats.Some.Avg10, 0.1},
+			{"Avg60", stats.Some.Avg60, 2.0},
+			{"Avg300", stats.Some.Avg300, 3.85},
+			{"Total", float64(stats.Some.Total), 15.0},
+		}
+
+		for _, tc := range testCases {
+			t.Run(tc.name, func(t *testing.T) {
+				if tc.got != tc.want {
+					t.Errorf("got: %f, want: %f", tc.got, tc.want)
+				}
+			})
+		}
+	})
+
+	res := []string{"memory", "io"}
+
+	for _, resource := range res {
+		t.Run(resource, func(t *testing.T) {
+			stats, err := getProcFixtures(t).NewPSIStatsForResource(resource)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			if stats.Full == nil {
+				t.Fatalf("%s resource must not have nil 'full' stats", resource)
+			}
+
+			if stats.Some == nil {
+				t.Fatalf("%s resource must not have nil 'some' stats", resource)
+			}
+
+			testCases := []struct {
+				name string
+				got  float64
+				want float64
+			}{
+				{"some/Avg10", stats.Some.Avg10, 0.1},
+				{"some/Avg60", stats.Some.Avg60, 2.0},
+				{"some/Avg300", stats.Some.Avg300, 3.85},
+				{"some/Total", float64(stats.Some.Total), 15.0},
+				{"full/Avg10", stats.Full.Avg10, 0.2},
+				{"full/Avg60", stats.Full.Avg60, 3.0},
+				{"full/Avg300", stats.Full.Avg300, 4.95},
+				{"full/Total", float64(stats.Full.Total), 25.0},
+			}
+
+			for _, tc := range testCases {
+				t.Run(tc.name, func(t *testing.T) {
+					if tc.got != tc.want {
+						t.Errorf("got: %f, want: %f", tc.got, tc.want)
+					}
+				})
+			}
+		})
+	}
+}
+
+// TestParsePSIStats tests the edge cases that we won't run into when running TestPSIStats
+func TestParsePSIStats(t *testing.T) {
+	t.Run("unknown measurement type", func(t *testing.T) {
+		raw := "nonesense haha test=fake"
+		_, err := parsePSIStats("fake", strings.NewReader(raw))
+		if err != nil {
+			t.Error("unknown measurement type must be ignored")
+		}
+	})
+
+	t.Run("malformed measurement", func(t *testing.T) {
+		t.Run("some", func(t *testing.T) {
+			raw := `some avg10=0.10 avg60=2.00 avg300=3.85 total=oops
+full avg10=0.20 avg60=3.00 avg300=teddy total=25`
+			stats, err := parsePSIStats("fake", strings.NewReader(raw))
+			if err == nil {
+				t.Error("a malformed line must result in a parse error")
+			}
+
+			if stats.Some != nil || stats.Full != nil {
+				t.Error("a parse error must result in a nil PSILine")
+			}
+		})
+		t.Run("full", func(t *testing.T) {
+			raw := `some avg10=0.10 avg60=2.00 avg300=3.85 total=1
+full avg10=0.20 avg60=3.00 avg300=test total=25`
+			stats, err := parsePSIStats("fake", strings.NewReader(raw))
+			t.Log(err)
+			t.Log(stats)
+			if err == nil {
+				t.Error("a malformed line must result in a parse error")
+			}
+
+			if stats.Some != nil || stats.Full != nil {
+				t.Error("a parse error must result in a nil PSILine")
+			}
+		})
+
+	})
+}
diff --git a/proc_stat_test.go b/proc_stat_test.go
index e2df884..3822f55 100644
--- a/proc_stat_test.go
+++ b/proc_stat_test.go
@@ -19,7 +19,7 @@ import (
 )
 
 func TestProcStat(t *testing.T) {
-	p, err := FS("fixtures").NewProc(26231)
+	p, err := getProcFixtures(t).NewProc(26231)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -38,7 +38,7 @@ func TestProcStat(t *testing.T) {
 		{name: "user time", want: 1677, have: int(s.UTime)},
 		{name: "system time", want: 44, have: int(s.STime)},
 		{name: "start time", want: 82375, have: int(s.Starttime)},
-		{name: "virtual memory size", want: 56274944, have: s.VSize},
+		{name: "virtual memory size", want: 56274944, have: int(s.VSize)},
 		{name: "resident set size", want: 1981, have: s.RSS},
 	} {
 		if test.want != test.have {
@@ -71,7 +71,7 @@ func TestProcStatVirtualMemory(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	if want, have := 56274944, s.VirtualMemory(); want != have {
+	if want, have := 56274944, int(s.VirtualMemory()); want != have {
 		t.Errorf("want virtual memory %d, have %d", want, have)
 	}
 }
@@ -114,7 +114,11 @@ func TestProcStatCPUTime(t *testing.T) {
 }
 
 func testProcStat(pid int) (ProcStat, error) {
-	p, err := FS("fixtures").NewProc(pid)
+	fs, err := NewFS(procTestFixtures)
+	if err != nil {
+		return ProcStat{}, err
+	}
+	p, err := fs.NewProc(pid)
 	if err != nil {
 		return ProcStat{}, err
 	}
diff --git a/proc_status_test.go b/proc_status_test.go
new file mode 100644
index 0000000..fdf2ac4
--- /dev/null
+++ b/proc_status_test.go
@@ -0,0 +1,76 @@
+// Copyright 2018 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package procfs
+
+import (
+	"testing"
+)
+
+func TestProcStatus(t *testing.T) {
+	p, err := getProcFixtures(t).NewProc(26231)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	s, err := p.NewStatus()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	for _, test := range []struct {
+		name string
+		want int
+		have int
+	}{
+		{name: "pid", want: 26231, have: s.PID},
+		{name: "VmPeak", want: 58472 * 1024, have: int(s.VmPeak)},
+		{name: "VmSize", want: 58440 * 1024, have: int(s.VmSize)},
+		{name: "VmLck", want: 0 * 1024, have: int(s.VmLck)},
+		{name: "VmPin", want: 0 * 1024, have: int(s.VmPin)},
+		{name: "VmHWM", want: 8028 * 1024, have: int(s.VmHWM)},
+		{name: "VmRSS", want: 6716 * 1024, have: int(s.VmRSS)},
+		{name: "RssAnon", want: 2092 * 1024, have: int(s.RssAnon)},
+		{name: "RssFile", want: 4624 * 1024, have: int(s.RssFile)},
+		{name: "RssShmem", want: 0 * 1024, have: int(s.RssShmem)},
+		{name: "VmData", want: 2580 * 1024, have: int(s.VmData)},
+		{name: "VmStk", want: 136 * 1024, have: int(s.VmStk)},
+		{name: "VmExe", want: 948 * 1024, have: int(s.VmExe)},
+		{name: "VmLib", want: 6816 * 1024, have: int(s.VmLib)},
+		{name: "VmPTE", want: 128 * 1024, have: int(s.VmPTE)},
+		{name: "VmPMD", want: 12 * 1024, have: int(s.VmPMD)},
+		{name: "VmSwap", want: 660 * 1024, have: int(s.VmSwap)},
+		{name: "HugetlbPages", want: 0 * 1024, have: int(s.HugetlbPages)},
+		{name: "VoluntaryCtxtSwitches", want: 4742839, have: int(s.VoluntaryCtxtSwitches)},
+		{name: "NonVoluntaryCtxtSwitches", want: 1727500, have: int(s.NonVoluntaryCtxtSwitches)},
+		{name: "TotalCtxtSwitches", want: 4742839 + 1727500, have: int(s.TotalCtxtSwitches())},
+	} {
+		if test.want != test.have {
+			t.Errorf("want %s %d, have %d", test.name, test.want, test.have)
+		}
+	}
+}
+
+func TestProcStatusName(t *testing.T) {
+	p, err := getProcFixtures(t).NewProc(26231)
+	if err != nil {
+		t.Fatal(err)
+	}
+	s, err := p.NewStatus()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if want, have := "prometheus", s.Name; want != have {
+		t.Errorf("want name %s, have %s", want, have)
+	}
+}
diff --git a/proc_test.go b/proc_test.go
index ee7e69d..f4da574 100644
--- a/proc_test.go
+++ b/proc_test.go
@@ -20,7 +20,7 @@ import (
 )
 
 func TestSelf(t *testing.T) {
-	fs := FS("fixtures")
+	fs := getProcFixtures(t)
 
 	p1, err := fs.NewProc(26231)
 	if err != nil {
@@ -37,7 +37,7 @@ func TestSelf(t *testing.T) {
 }
 
 func TestAllProcs(t *testing.T) {
-	procs, err := FS("fixtures").AllProcs()
+	procs, err := getProcFixtures(t).AllProcs()
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -58,7 +58,7 @@ func TestCmdLine(t *testing.T) {
 		{process: 26232, want: []string{}},
 		{process: 26233, want: []string{"com.github.uiautomator"}},
 	} {
-		p1, err := FS("fixtures").NewProc(tt.process)
+		p1, err := getProcFixtures(t).NewProc(tt.process)
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -80,7 +80,7 @@ func TestComm(t *testing.T) {
 		{process: 26231, want: "vim"},
 		{process: 26232, want: "ata_sff"},
 	} {
-		p1, err := FS("fixtures").NewProc(tt.process)
+		p1, err := getProcFixtures(t).NewProc(tt.process)
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -102,7 +102,7 @@ func TestExecutable(t *testing.T) {
 		{process: 26231, want: "/usr/bin/vim"},
 		{process: 26232, want: ""},
 	} {
-		p, err := FS("fixtures").NewProc(tt.process)
+		p, err := getProcFixtures(t).NewProc(tt.process)
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -111,13 +111,69 @@ func TestExecutable(t *testing.T) {
 			t.Fatal(err)
 		}
 		if !reflect.DeepEqual(tt.want, exe) {
-			t.Errorf("want absolute path to cmdline %v, have %v", tt.want, exe)
+			t.Errorf("want absolute path to exe %v, have %v", tt.want, exe)
+		}
+	}
+}
+
+func TestCwd(t *testing.T) {
+	for _, tt := range []struct {
+		process    int
+		want       string
+		brokenLink bool
+	}{
+		{process: 26231, want: "/usr/bin"},
+		{process: 26232, want: "/does/not/exist", brokenLink: true},
+		{process: 26233, want: ""},
+	} {
+		p, err := getProcFixtures(t).NewProc(tt.process)
+		if err != nil {
+			t.Fatal(err)
+		}
+		wd, err := p.Cwd()
+		if err != nil {
+			t.Fatal(err)
+		}
+		if !reflect.DeepEqual(tt.want, wd) {
+			if wd == "" && tt.brokenLink {
+				// Allow the result to be empty when can't os.Readlink broken links
+				continue
+			}
+			t.Errorf("want absolute path to cwd %v, have %v", tt.want, wd)
+		}
+	}
+}
+
+func TestRoot(t *testing.T) {
+	for _, tt := range []struct {
+		process    int
+		want       string
+		brokenLink bool
+	}{
+		{process: 26231, want: "/"},
+		{process: 26232, want: "/does/not/exist", brokenLink: true},
+		{process: 26233, want: ""},
+	} {
+		p, err := getProcFixtures(t).NewProc(tt.process)
+		if err != nil {
+			t.Fatal(err)
+		}
+		rdir, err := p.RootDir()
+		if err != nil {
+			t.Fatal(err)
+		}
+		if !reflect.DeepEqual(tt.want, rdir) {
+			if rdir == "" && tt.brokenLink {
+				// Allow the result to be empty when can't os.Readlink broken links
+				continue
+			}
+			t.Errorf("want absolute path to rootdir %v, have %v", tt.want, rdir)
 		}
 	}
 }
 
 func TestFileDescriptors(t *testing.T) {
-	p1, err := FS("fixtures").NewProc(26231)
+	p1, err := getProcFixtures(t).NewProc(26231)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -132,7 +188,7 @@ func TestFileDescriptors(t *testing.T) {
 }
 
 func TestFileDescriptorTargets(t *testing.T) {
-	p1, err := FS("fixtures").NewProc(26231)
+	p1, err := getProcFixtures(t).NewProc(26231)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -154,7 +210,7 @@ func TestFileDescriptorTargets(t *testing.T) {
 }
 
 func TestFileDescriptorsLen(t *testing.T) {
-	p1, err := FS("fixtures").NewProc(26231)
+	p1, err := getProcFixtures(t).NewProc(26231)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/stat_test.go b/stat_test.go
index 2043b5e..00c280b 100644
--- a/stat_test.go
+++ b/stat_test.go
@@ -16,7 +16,7 @@ package procfs
 import "testing"
 
 func TestStat(t *testing.T) {
-	s, err := FS("fixtures").NewStat()
+	s, err := getProcFixtures(t).NewStat()
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/sysfs/class_power_supply_test.go b/sysfs/class_power_supply_test.go
new file mode 100644
index 0000000..e8402a6
--- /dev/null
+++ b/sysfs/class_power_supply_test.go
@@ -0,0 +1,80 @@
+// Copyright 2018 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build !windows
+
+package sysfs
+
+import (
+	"encoding/json"
+	"reflect"
+	"testing"
+)
+
+func TestNewPowerSupplyClass(t *testing.T) {
+	fs, err := NewFS(sysTestFixtures)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	psc, err := fs.NewPowerSupplyClass()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var (
+		acOnline             int64 = 0
+		bat0Capacity         int64 = 98
+		bat0CycleCount       int64 = 0
+		bat0EnergyFull       int64 = 50060000
+		bat0EnergyFullDesign int64 = 47520000
+		bat0EnergyNow        int64 = 49450000
+		bat0PowerNow         int64 = 4830000
+		bat0Present          int64 = 1
+		bat0VoltageMinDesign int64 = 10800000
+		bat0VoltageNow       int64 = 12229000
+	)
+
+	powerSupplyClass := PowerSupplyClass{
+		"AC": {
+			Name:   "AC",
+			Type:   "Mains",
+			Online: &acOnline,
+		},
+		"BAT0": {
+			Name:             "BAT0",
+			Capacity:         &bat0Capacity,
+			CapacityLevel:    "Normal",
+			CycleCount:       &bat0CycleCount,
+			EnergyFull:       &bat0EnergyFull,
+			EnergyFullDesign: &bat0EnergyFullDesign,
+			EnergyNow:        &bat0EnergyNow,
+			Manufacturer:     "LGC",
+			ModelName:        "LNV-45N1",
+			PowerNow:         &bat0PowerNow,
+			Present:          &bat0Present,
+			SerialNumber:     "38109",
+			Status:           "Discharging",
+			Technology:       "Li-ion",
+			Type:             "Battery",
+			VoltageMinDesign: &bat0VoltageMinDesign,
+			VoltageNow:       &bat0VoltageNow,
+		},
+	}
+
+	if !reflect.DeepEqual(powerSupplyClass, psc) {
+		want, _ := json.Marshal(powerSupplyClass)
+		get, _ := json.Marshal(psc)
+		t.Errorf("Result not correct: want %v, have %v.", string(want), string(get))
+	}
+}
diff --git a/sysfs/class_thermal_test.go b/sysfs/class_thermal_test.go
new file mode 100644
index 0000000..4e6b0d6
--- /dev/null
+++ b/sysfs/class_thermal_test.go
@@ -0,0 +1,61 @@
+// Copyright 2018 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build !windows
+
+package sysfs
+
+import (
+	"reflect"
+	"testing"
+
+	"github.com/prometheus/procfs/internal/util"
+)
+
+func TestClassThermalZoneStats(t *testing.T) {
+	fs, err := NewFS(sysTestFixtures)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	thermalTest, err := fs.NewClassThermalZoneStats()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	enabled := util.ParseBool("enabled")
+	passive := uint64(0)
+
+	classThermalZoneStats := []ClassThermalZoneStats{
+		{
+			Name:    "0",
+			Type:    "bcm2835_thermal",
+			Policy:  "step_wise",
+			Temp:    49925,
+			Mode:    nil,
+			Passive: nil,
+		},
+		{
+			Name:    "1",
+			Type:    "acpitz",
+			Policy:  "step_wise",
+			Temp:    44000,
+			Mode:    enabled,
+			Passive: &passive,
+		},
+	}
+
+	if !reflect.DeepEqual(classThermalZoneStats, thermalTest) {
+		t.Errorf("Result not correct: want %v, have %v", classThermalZoneStats, thermalTest)
+	}
+}
diff --git a/sysfs/fs_test.go b/sysfs/fs_test.go
index 2b7402e..2ef5a73 100644
--- a/sysfs/fs_test.go
+++ b/sysfs/fs_test.go
@@ -15,6 +15,10 @@ package sysfs
 
 import "testing"
 
+const (
+	sysTestFixtures = "../fixtures/sys"
+)
+
 func TestNewFS(t *testing.T) {
 	if _, err := NewFS("foobar"); err == nil {
 		t.Error("want NewFS to fail for non-existing mount point")
@@ -23,86 +27,8 @@ func TestNewFS(t *testing.T) {
 	if _, err := NewFS("doc.go"); err == nil {
 		t.Error("want NewFS to fail if mount point is not a directory")
 	}
-}
-
-func TestFSXFSStats(t *testing.T) {
-	stats, err := FS("fixtures").XFSStats()
-	if err != nil {
-		t.Fatalf("failed to parse XFS stats: %v", err)
-	}
-
-	tests := []struct {
-		name      string
-		allocated uint32
-	}{
-		{
-			name:      "sda1",
-			allocated: 1,
-		},
-		{
-			name:      "sdb1",
-			allocated: 2,
-		},
-	}
-
-	const expect = 2
-
-	if l := len(stats); l != expect {
-		t.Fatalf("unexpected number of XFS stats: %d", l)
-	}
-	if l := len(tests); l != expect {
-		t.Fatalf("unexpected number of tests: %d", l)
-	}
-
-	for i, tt := range tests {
-		if want, got := tt.name, stats[i].Name; want != got {
-			t.Errorf("unexpected stats name:\nwant: %q\nhave: %q", want, got)
-		}
-
-		if want, got := tt.allocated, stats[i].ExtentAllocation.ExtentsAllocated; want != got {
-			t.Errorf("unexpected extents allocated:\nwant: %d\nhave: %d", want, got)
-		}
-	}
-}
-
-func TestFSBcacheStats(t *testing.T) {
-	stats, err := FS("fixtures").BcacheStats()
-	if err != nil {
-		t.Fatalf("failed to parse bcache stats: %v", err)
-	}
-
-	tests := []struct {
-		name   string
-		bdevs  int
-		caches int
-	}{
-		{
-			name:   "deaddd54-c735-46d5-868e-f331c5fd7c74",
-			bdevs:  1,
-			caches: 1,
-		},
-	}
-
-	const expect = 1
-
-	if l := len(stats); l != expect {
-		t.Fatalf("unexpected number of bcache stats: %d", l)
-	}
-	if l := len(tests); l != expect {
-		t.Fatalf("unexpected number of tests: %d", l)
-	}
-
-	for i, tt := range tests {
-		if want, got := tt.name, stats[i].Name; want != got {
-			t.Errorf("unexpected stats name:\nwant: %q\nhave: %q", want, got)
-		}
-
-		if want, got := tt.bdevs, len(stats[i].Bdevs); want != got {
-			t.Errorf("unexpected value allocated:\nwant: %d\nhave: %d", want, got)
-		}
 
-		if want, got := tt.caches, len(stats[i].Caches); want != got {
-			t.Errorf("unexpected value allocated:\nwant: %d\nhave: %d", want, got)
-		}
+	if _, err := NewFS(sysTestFixtures); err != nil {
+		t.Error("want NewFS to succeed if mount point exists")
 	}
 }
diff --git a/sysfs/net_class_test.go b/sysfs/net_class_test.go
index 5c356dc..3f376e2 100644
--- a/sysfs/net_class_test.go
+++ b/sysfs/net_class_test.go
@@ -11,6 +11,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build !windows
+
 package sysfs
 
 import (
@@ -18,8 +20,27 @@ import (
 	"testing"
 )
 
+func TestNewNetClassDevices(t *testing.T) {
+	fs, err := NewFS(sysTestFixtures)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	devices, err := fs.NetClassDevices()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(devices) != 1 {
+		t.Errorf("Unexpected number of devices, want %d, have %d", 1, len(devices))
+	}
+	if devices[0] != "eth0" {
+		t.Errorf("Found unexpected device, want %s, have %s", "eth0", devices[0])
+	}
+}
+
 func TestNewNetClass(t *testing.T) {
-	fs, err := NewFS("fixtures")
+	fs, err := NewFS(sysTestFixtures)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -29,35 +50,56 @@ func TestNewNetClass(t *testing.T) {
 		t.Fatal(err)
 	}
 
+	var (
+		addrAssignType   int64 = 3
+		addrLen          int64 = 6
+		carrier          int64 = 1
+		carrierChanges   int64 = 2
+		carrierDownCount int64 = 1
+		carrierUpCount   int64 = 1
+		devID            int64 = 32
+		dormant          int64 = 1
+		flags            int64 = 4867
+		ifIndex          int64 = 2
+		ifLink           int64 = 2
+		linkMode         int64 = 1
+		mtu              int64 = 1500
+		nameAssignType   int64 = 2
+		netDevGroup      int64 = 0
+		speed            int64 = 1000
+		txQueueLen       int64 = 1000
+		netType          int64 = 1
+	)
+
 	netClass := NetClass{
 		"eth0": {
 			Address:          "01:01:01:01:01:01",
-			AddrAssignType:   3,
-			AddrLen:          6,
+			AddrAssignType:   &addrAssignType,
+			AddrLen:          &addrLen,
 			Broadcast:        "ff:ff:ff:ff:ff:ff",
-			Carrier:          1,
-			CarrierChanges:   2,
-			CarrierDownCount: 1,
-			CarrierUpCount:   1,
-			DevID:            32,
-			Dormant:          1,
+			Carrier:          &carrier,
+			CarrierChanges:   &carrierChanges,
+			CarrierDownCount: &carrierDownCount,
+			CarrierUpCount:   &carrierUpCount,
+			DevID:            &devID,
+			Dormant:          &dormant,
 			Duplex:           "full",
-			Flags:            4867,
+			Flags:            &flags,
 			IfAlias:          "",
-			IfIndex:          2,
-			IfLink:           2,
-			LinkMode:         1,
-			MTU:              1500,
+			IfIndex:          &ifIndex,
+			IfLink:           &ifLink,
+			LinkMode:         &linkMode,
+			MTU:              &mtu,
 			Name:             "eth0",
-			NameAssignType:   2,
-			NetDevGroup:      0,
+			NameAssignType:   &nameAssignType,
+			NetDevGroup:      &netDevGroup,
 			OperState:        "up",
 			PhysPortID:       "",
 			PhysPortName:     "",
 			PhysSwitchID:     "",
-			Speed:            1000,
-			TxQueueLen:       1000,
-			Type:             1,
+			Speed:            &speed,
+			TxQueueLen:       &txQueueLen,
+			Type:             &netType,
 		},
 	}
 
diff --git a/sysfs/system_cpu_test.go b/sysfs/system_cpu_test.go
new file mode 100644
index 0000000..f4ecfc5
--- /dev/null
+++ b/sysfs/system_cpu_test.go
@@ -0,0 +1,76 @@
+// Copyright 2018 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build !windows
+
+package sysfs
+
+import (
+	"reflect"
+	"testing"
+)
+
+func makeUint64(v uint64) *uint64 {
+	return &v
+}
+
+func TestNewSystemCpufreq(t *testing.T) {
+	fs, err := NewFS(sysTestFixtures)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	c, err := fs.NewSystemCpufreq()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	systemCpufreq := []SystemCPUCpufreqStats{
+		// Has missing `cpuinfo_cur_freq` file.
+		{
+			Name:                     "0",
+			CpuinfoCurrentFrequency:  nil,
+			CpuinfoMinimumFrequency:  makeUint64(800000),
+			CpuinfoMaximumFrequency:  makeUint64(2400000),
+			CpuinfoTransitionLatency: makeUint64(0),
+			ScalingCurrentFrequency:  makeUint64(1219917),
+			ScalingMinimumFrequency:  makeUint64(800000),
+			ScalingMaximumFrequency:  makeUint64(2400000),
+			AvailableGovernors:       "performance powersave",
+			Driver:                   "intel_pstate",
+			Governor:                 "powersave",
+			RelatedCpus:              "0",
+			SetSpeed:                 "<unsupported>",
+		},
+		// Has missing `scaling_cur_freq` file.
+		{
+			Name:                     "1",
+			CpuinfoCurrentFrequency:  makeUint64(1200195),
+			CpuinfoMinimumFrequency:  makeUint64(1200000),
+			CpuinfoMaximumFrequency:  makeUint64(3300000),
+			CpuinfoTransitionLatency: makeUint64(4294967295),
+			ScalingCurrentFrequency:  nil,
+			ScalingMinimumFrequency:  makeUint64(1200000),
+			ScalingMaximumFrequency:  makeUint64(3300000),
+			AvailableGovernors:       "performance powersave",
+			Driver:                   "intel_pstate",
+			Governor:                 "powersave",
+			RelatedCpus:              "1",
+			SetSpeed:                 "<unsupported>",
+		},
+	}
+
+	if !reflect.DeepEqual(systemCpufreq, c) {
+		t.Errorf("Result not correct: want %v, have %v", systemCpufreq, c)
+	}
+}
diff --git a/xfrm_test.go b/xfrm_test.go
index 5918c39..4f06b96 100644
--- a/xfrm_test.go
+++ b/xfrm_test.go
@@ -18,7 +18,7 @@ import (
 )
 
 func TestXfrmStats(t *testing.T) {
-	xfrmStats, err := FS("fixtures").NewXfrmStat()
+	xfrmStats, err := getProcFixtures(t).NewXfrmStat()
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/xfs/parse_test.go b/xfs/parse_test.go
index 2e946c2..fcbe9ef 100644
--- a/xfs/parse_test.go
+++ b/xfs/parse_test.go
@@ -18,7 +18,6 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/prometheus/procfs"
 	"github.com/prometheus/procfs/xfs"
 )
 
@@ -425,7 +424,14 @@ func TestParseStats(t *testing.T) {
 			stats, err = xfs.ParseStats(strings.NewReader(tt.s))
 		}
 		if tt.fs {
-			stats, err = procfs.FS("../fixtures").XFSStats()
+			xfs, err := xfs.NewFS("../fixtures/proc", "../fixtures/sys")
+			if err != nil {
+				t.Fatalf("failed to access xfs fs: %v", err)
+			}
+			stats, err = xfs.ProcStat()
+			if err != nil {
+				t.Fatalf("failed to gather xfs stats: %v", err)
+			}
 		}
 
 		if tt.invalid && err == nil {
diff --git a/xfs/xfs_test.go b/xfs/xfs_test.go
new file mode 100644
index 0000000..03fe81e
--- /dev/null
+++ b/xfs/xfs_test.go
@@ -0,0 +1,82 @@
+// Copyright 2019 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package xfs provides access to statistics exposed by the XFS filesystem.
+package xfs_test
+
+import (
+	"testing"
+
+	"github.com/prometheus/procfs/xfs"
+)
+
+func TestReadProcStat(t *testing.T) {
+	xfs, err := xfs.NewFS("../fixtures/proc", "../fixtures/sys")
+	if err != nil {
+		t.Fatalf("failed to access xfs fs: %v", err)
+	}
+	stats, err := xfs.ProcStat()
+	if err != nil {
+		t.Fatalf("failed to parse XFS stats: %v", err)
+	}
+
+	// Very lightweight test just to sanity check the path used
+	// to open XFS stats. Heavier tests in package xfs.
+	if want, got := uint32(92447), stats.ExtentAllocation.ExtentsAllocated; want != got {
+		t.Errorf("unexpected extents allocated:\nwant: %d\nhave: %d", want, got)
+	}
+}
+
+func TestReadSysStats(t *testing.T) {
+	xfs, err := xfs.NewFS("../fixtures/proc", "../fixtures/sys")
+	if err != nil {
+		t.Fatalf("failed to access xfs fs: %v", err)
+	}
+	stats, err := xfs.SysStats()
+	if err != nil {
+		t.Fatalf("failed to parse XFS stats: %v", err)
+	}
+
+	tests := []struct {
+		name      string
+		allocated uint32
+	}{
+		{
+			name:      "sda1",
+			allocated: 1,
+		},
+		{
+			name:      "sdb1",
+			allocated: 2,
+		},
+	}
+
+	const expect = 2
+
+	if l := len(stats); l != expect {
+		t.Fatalf("unexpected number of XFS stats: %d", l)
+	}
+	if l := len(tests); l != expect {
+		t.Fatalf("unexpected number of tests: %d", l)
+	}
+
+	for i, tt := range tests {
+		if want, got := tt.name, stats[i].Name; want != got {
+			t.Errorf("unexpected stats name:\nwant: %q\nhave: %q", want, got)
+		}
+
+		if want, got := tt.allocated, stats[i].ExtentAllocation.ExtentsAllocated; want != got {
+			t.Errorf("unexpected extents allocated:\nwant: %d\nhave: %d", want, got)
+		}
+	}
+}