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,