package fsfilters import "strings" var readOnlyFilesystemPatterns = []struct { reason string substrings []string }{ {reason: "erofs", substrings: []string{"erofs"}}, {reason: "squashfs", substrings: []string{"squashfs", "squash-fs"}}, {reason: "iso9660", substrings: []string{"iso9660"}}, {reason: "cdfs", substrings: []string{"cdfs"}}, {reason: "udf", substrings: []string{"udf"}}, {reason: "cramfs", substrings: []string{"cramfs"}}, {reason: "romfs", substrings: []string{"romfs"}}, } // ReadOnlyFilesystemReason returns a label explaining why a filesystem should be // ignored for usage calculations, along with a boolean indicating whether it is // a read-only filesystem that always reports full usage. This helps us avoid // false positives from immutable root images (overlay/squashfs/erofs) that ship // with Home Assistant OS and similar appliances, as well as snap mounts on Ubuntu // systems (see issues #505, #690). func ReadOnlyFilesystemReason(fsType string, totalBytes, usedBytes uint64) (string, bool) { ft := strings.ToLower(strings.TrimSpace(fsType)) if ft == "" { return "", false } // Common read-only filesystem types used for immutable system partitions. for _, pattern := range readOnlyFilesystemPatterns { for _, needle := range pattern.substrings { if strings.Contains(ft, needle) { return pattern.reason, true } } } // Overlay-style filesystems can report 100% usage even though writes are // redirected elsewhere. Treat them as read-only when the reported usage is // saturated so we fall back to the writable layer metrics instead. if strings.Contains(ft, "overlay") || strings.Contains(ft, "overlayfs") { if totalBytes > 0 && usedBytes >= totalBytes { return "overlay", true } } return "", false } // ShouldIgnoreReadOnlyFilesystem reports whether the filesystem should be // skipped from usage aggregation. func ShouldIgnoreReadOnlyFilesystem(fsType string, totalBytes, usedBytes uint64) bool { _, skip := ReadOnlyFilesystemReason(fsType, totalBytes, usedBytes) return skip } // virtualFSTypes are filesystem types that represent virtual/pseudo filesystems // which should not be counted toward disk usage. var virtualFSTypes = map[string]bool{ "tmpfs": true, "devtmpfs": true, "cgroup": true, "cgroup2": true, "sysfs": true, "proc": true, "devpts": true, "securityfs": true, "debugfs": true, "tracefs": true, "fusectl": true, "configfs": true, "pstore": true, "hugetlbfs": true, "mqueue": true, "bpf": true, } // networkFSPatterns are substrings that indicate network/remote filesystems. var networkFSPatterns = []string{"fuse", "9p", "nfs", "cifs", "smb"} // specialMountPrefixes are mountpoint prefixes that indicate system mounts. var specialMountPrefixes = []string{ "/dev", "/proc", "/sys", "/run", "/var/lib/docker", "/var/lib/containers", "/snap", } // containerOverlayPatterns detect container overlay filesystem paths from various // container runtimes (Docker, Podman, LXC, EnhanceCP, etc.) that may not be in // standard locations. These paths should be excluded from disk usage as they // represent container layers, not actual storage usage. var containerOverlayPatterns = []string{ "/overlay2/", "/overlay/", "/diff/", "/merged", } // containerPathPrefixes detect container-related paths that should be excluded // from disk usage, even if they don't contain "/containers/". var containerPathPrefixes = []string{ "/mnt/.ix-apps/docker/", // TrueNAS SCALE Docker overlay mounts } // ShouldSkipFilesystem determines if a filesystem should be excluded from disk // usage aggregation. It checks for read-only filesystems, virtual/pseudo filesystems, // network mounts, and special system mountpoints. Returns skip=true if the filesystem // should be excluded, along with a list of reason strings. func ShouldSkipFilesystem(fsType, mountpoint string, totalBytes, usedBytes uint64) (skip bool, reasons []string) { fsTypeLower := strings.ToLower(strings.TrimSpace(fsType)) // Check read-only filesystems (existing logic) if reason, isReadOnly := ReadOnlyFilesystemReason(fsType, totalBytes, usedBytes); isReadOnly { reasons = append(reasons, "read-only-"+reason) } // Check virtual filesystem types if virtualFSTypes[fsTypeLower] { reasons = append(reasons, "special-fs-type") } // Check network filesystem patterns for _, pattern := range networkFSPatterns { if strings.Contains(fsTypeLower, pattern) { reasons = append(reasons, "special-fs-type") break } } // Check special mountpoint prefixes for _, prefix := range specialMountPrefixes { if strings.HasPrefix(mountpoint, prefix) { reasons = append(reasons, "special-mountpoint") break } } // Check specific special mountpoints if mountpoint == "/boot/efi" { reasons = append(reasons, "special-mountpoint") } // Windows System Reserved partition if mountpoint == "System Reserved" || strings.Contains(mountpoint, "System Reserved") { reasons = append(reasons, "special-mountpoint") } // Check for container overlay paths from various runtimes in non-standard locations // (e.g., /var/local/enhance/containers/*/overlay/merged). Related to #790. if strings.Contains(mountpoint, "/containers/") { for _, pattern := range containerOverlayPatterns { if strings.Contains(mountpoint, pattern) { reasons = append(reasons, "container-overlay") break } } } // Check for container paths that don't follow the /containers/ pattern // (e.g., TrueNAS SCALE uses /mnt/.ix-apps/docker/overlay2/...). Related to #718. for _, prefix := range containerPathPrefixes { if strings.HasPrefix(mountpoint, prefix) { reasons = append(reasons, "container-overlay") break } } return len(reasons) > 0, reasons }