Fix double-counted Linux disk space from bind-mounted filesystems (#44969)
**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: <img width="211" height="108" alt="Screenshot 2026-05-07 at 9 40 12 PM" src="https://github.com/user-attachments/assets/81cad4af-f146-4ea9-84e3-ee56eb426d8a" /> With the fix applied on this branch, refetched again and saw the correct total disk space: <img width="251" height="118" alt="Screenshot 2026-05-07 at 9 31 15 PM" src="https://github.com/user-attachments/assets/cbb6f91b-320a-4d48-8583-f5fdf5a4fcd9" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Bug Fixes - Resolved inaccurate total disk space reporting on Linux systems where the same filesystem is bind-mounted at multiple paths. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -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_*`).
|
||||
@@ -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;
|
||||
```
|
||||
|
||||
|
||||
+17
-13
@@ -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) }'
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user