From c19df6d2c70dff44e11599122c32ddd3430c6779 Mon Sep 17 00:00:00 2001
From: Nico <32375741+nulmete@users.noreply.github.com>
Date: Fri, 15 May 2026 08:48:17 +0200
Subject: [PATCH] Fix double-counted Linux disk space from bind-mounted
filesystems (#44969)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
**Related issue:** Resolves #43091
# Checklist for submitter
- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
## Testing
- [ ] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
Was able to reproduce by enrolling a Ubuntu 25.10 host and mounting /
onto a different path like this:
```bash
sudo mkdir -p /tmp/snap.rootfs_TESTING
sudo mount --bind / /tmp/snap.rootfs_TESTING
```
Then, refetched vitals and saw the total disk space doubled:
With the fix applied on this branch, refetched again and saw the correct
total disk space:
## Summary by CodeRabbit
## Bug Fixes
- Resolved inaccurate total disk space reporting on Linux systems where
the same filesystem is bind-mounted at multiple paths.
---
changes/43091-inaccurate-total-disk-space | 1 +
.../understanding-host-vitals.md | 4 +--
docs/queries.yml | 30 +++++++++++--------
server/service/osquery_utils/queries.go | 3 +-
4 files changed, 22 insertions(+), 16 deletions(-)
create mode 100644 changes/43091-inaccurate-total-disk-space
diff --git a/changes/43091-inaccurate-total-disk-space b/changes/43091-inaccurate-total-disk-space
new file mode 100644
index 0000000000..837d75a26a
--- /dev/null
+++ b/changes/43091-inaccurate-total-disk-space
@@ -0,0 +1 @@
+* Fixed Linux total disk space being double-counted when a filesystem was bind-mounted at multiple paths (e.g. snap-confine's `/tmp/snap.rootfs_*`).
diff --git a/docs/Contributing/product-groups/orchestration/understanding-host-vitals.md b/docs/Contributing/product-groups/orchestration/understanding-host-vitals.md
index f332682fb5..821883fdf7 100644
--- a/docs/Contributing/product-groups/orchestration/understanding-host-vitals.md
+++ b/docs/Contributing/product-groups/orchestration/understanding-host-vitals.md
@@ -175,7 +175,7 @@ SELECT (blocks_available * 100 / blocks) AS percent_disk_space_available,
SELECT (blocks_available * 100 / blocks) AS percent_disk_space_available,
round((blocks_available * blocks_size * 10e-10),2) AS gigs_disk_space_available,
round((blocks * blocks_size * 10e-10),2) AS gigs_total_disk_space,
- (SELECT round(SUM(blocks * blocks_size) * 10e-10, 2) FROM mounts WHERE
+ (SELECT round(SUM(per_device_size) * 10e-10, 2) FROM (SELECT MAX(blocks * blocks_size) AS per_device_size FROM mounts WHERE
-- exclude mounts with no space
blocks > 0
AND blocks_size > 0
@@ -223,7 +223,7 @@ OR device LIKE '/dev/nvme%'
OR device LIKE '/dev/mapper%'
OR device LIKE '/dev/md%'
OR device LIKE '/dev/dm-%'
-)) AS gigs_all_disk_space
+) GROUP BY device)) AS gigs_all_disk_space
FROM mounts WHERE path = '/' LIMIT 1;
```
diff --git a/docs/queries.yml b/docs/queries.yml
index 37ad4ff55f..ab4a66b19a 100644
--- a/docs/queries.yml
+++ b/docs/queries.yml
@@ -113,7 +113,8 @@ spec:
SELECT (blocks_available * 100 / blocks) AS percent_disk_space_available,
round((blocks_available * blocks_size * 10e-10),2) AS gigs_disk_space_available,
round((blocks * blocks_size * 10e-10),2) AS gigs_total_disk_space,
- (SELECT round(SUM(blocks * blocks_size) * 10e-10, 2) FROM mounts WHERE
+ (SELECT round(SUM(per_device_size) * 10e-10, 2) FROM (
+ SELECT MAX(blocks * blocks_size) AS per_device_size FROM mounts WHERE
-- exclude mounts with no space
blocks > 0
AND blocks_size > 0
@@ -121,7 +122,7 @@ spec:
-- exclude external storage
AND path NOT LIKE '/media%' AND path NOT LIKE '/mnt%'
-- exclude device drivers
- AND path NOT LIKE '/dev%'
+ AND path NOT LIKE '/dev%'
-- exclude kernel-related mounts
AND path NOT LIKE '/proc%'
@@ -132,7 +133,7 @@ spec:
AND path NOT LIKE '/var/run%'
-- exclude boot files
- AND path NOT LIKE '/boot%'
+ AND path NOT LIKE '/boot%'
-- exclude snap packages
AND path NOT LIKE '/snap%' AND path NOT LIKE '/var/snap%'
@@ -142,24 +143,27 @@ spec:
AND path NOT LIKE '/var/lib/containers%'
AND type IN (
- 'ext4',
- 'ext3',
- 'ext2',
- 'xfs',
- 'btrfs',
- 'ntfs',
+ 'ext4',
+ 'ext3',
+ 'ext2',
+ 'xfs',
+ 'btrfs',
+ 'ntfs',
'vfat',
'fuseblk', --seen on NTFS and exFAT volumes mounted via FUSE
'zfs' --also valid storage
)
AND (
- device LIKE '/dev/sd%'
- OR device LIKE '/dev/hd%'
- OR device LIKE '/dev/vd%'
- OR device LIKE '/dev/nvme%'
+ device LIKE '/dev/sd%'
+ OR device LIKE '/dev/hd%'
+ OR device LIKE '/dev/vd%'
+ OR device LIKE '/dev/nvme%'
OR device LIKE '/dev/mapper%'
OR device LIKE '/dev/md%'
OR device LIKE '/dev/dm-%'
+ )
+ -- group by device so a filesystem bind-mounted at multiple paths is only counted once
+ GROUP BY device
)) AS gigs_all_disk_space
FROM mounts WHERE path = '/' LIMIT 1;
bash: df -P / | awk 'NR==2 { blocks=$2; blocks_available=$4; blocks_size=$2/$3 } END { printf "percent_disk_space_available=%.2f\n", (blocks_available * 100 / blocks); printf "gigs_disk_space_available=%.2f\n", (blocks_available * blocks_size * 10e-10); printf "gigs_total_disk_space=%.2f\n", (blocks * blocks_size * 10e-10) }'
diff --git a/server/service/osquery_utils/queries.go b/server/service/osquery_utils/queries.go
index e165cb6be6..3595df4438 100644
--- a/server/service/osquery_utils/queries.go
+++ b/server/service/osquery_utils/queries.go
@@ -442,11 +442,12 @@ var hostDetailQueries = map[string]DetailQuery{
Platforms: append(fleet.HostLinuxOSs, "darwin", "windows"), // not chrome
},
"disk_space_unix": {
+ // GROUP BY device collapses bind mounts so a filesystem mounted at multiple paths is counted once.
Query: fmt.Sprintf(`
SELECT (blocks_available * 100 / blocks) AS percent_disk_space_available,
round((blocks_available * blocks_size * 10e-10),2) AS gigs_disk_space_available,
round((blocks * blocks_size * 10e-10),2) AS gigs_total_disk_space,
- (SELECT round(SUM(blocks * blocks_size) * 10e-10, 2) FROM mounts %s) AS gigs_all_disk_space
+ (SELECT round(SUM(per_device_size) * 10e-10, 2) FROM (SELECT MAX(blocks * blocks_size) AS per_device_size FROM mounts %s GROUP BY device)) AS gigs_all_disk_space
FROM mounts WHERE path = '/' LIMIT 1;`, linuxGigsAllDiskSpaceSubQueryConditions),
Platforms: fleet.HostLinuxOSs,
DirectIngestFunc: directIngestDiskSpace,