package system // Which filesystem does a path lie on? — the identity the fill watch de-duplicates by (R-1371). // // WHY st_dev ALONE IS NOT THE ANSWER. Inside the controller's container "/" is an OVERLAY. An overlay has its own // device number and its own filesystem id, and both differ from the filesystem it lies on — while statfs reports that // filesystem's size and free space. Measured 2026-11-10 on demo-hp (guests 9201, 9202) and the N100 (felhom.eu // documentation/audits/space-list-2026-11-10/evidence/extra/fs-identity.txt): // // / type=overlayfs dev=139 blocks=18016108 free=10674453 // /mnt/sys_drive type=ext2/ext3 dev=64519 blocks=18016108 free=10674452 // /etc/hostname type=ext2/ext3 dev=64519 (the same on /etc/hosts and /etc/resolv.conf) // // One filesystem, two device numbers. The size-and-free numbers cannot be the identity either: the two `free` values // above are from ONE read loop and already differ by one block — a filesystem that is filling is being written. // // THE RULE. A path that is not on an overlay is identified by its device number. A path on an overlay is identified by // the device number of a file Docker itself keeps on its data-root and bind-mounts into every container // (dockerDataRootAnchors): the container's overlay lies on that same data-root. If no anchor can be read, there is NO // key — and a caller must then treat the path as a filesystem of its own (one warning too many, never one too few). // // LIMITS, stated: this holds for a container that Docker runs with Docker's own /etc files (the product's only // shape). A container whose /etc/hostname is bind-mounted from another filesystem by hand would get the wrong key. // dockerDataRootAnchors are the files Docker writes under /containers// and bind-mounts into the // container. Any one of them names the data-root's real device. var dockerDataRootAnchors = []string{"/etc/hostname", "/etc/hosts", "/etc/resolv.conf"} // FilesystemKey returns a number that is equal for two paths exactly when they lie on the same filesystem, as far as // this process can tell, and ok=false when it cannot tell. func FilesystemKey(path string) (key uint64, ok bool) { key, ok = filesystemKeyWith(path, statDev, isOverlayFS, dockerDataRootAnchors) debugf("[DEBUG] [system] FilesystemKey: path=%q key=%d ok=%v", path, key, ok) return key, ok } // filesystemKeyWith is the rule with its three inputs injected (the tests' seam): dev reads a path's device number, // overlay says whether a path lies on an overlay; both report ok=false for a path they cannot read. func filesystemKeyWith(path string, dev func(string) (uint64, bool), overlay func(string) (isOverlay, ok bool), anchors []string) (uint64, bool) { if path == "" { return 0, false } isOv, ok := overlay(path) if !ok { return 0, false } if !isOv { return dev(path) } for _, a := range anchors { if aOv, ok := overlay(a); !ok || aOv { continue // unreadable, or itself on the overlay (not bind-mounted): it names nothing } if d, ok := dev(a); ok { return d, true } } return 0, false }