Skip to content

Fix system df to count content blobs and deduplicate shared storage#1555

Open
realrajaryan wants to merge 1 commit into
apple:mainfrom
realrajaryan:fix/system-df-disk-usage
Open

Fix system df to count content blobs and deduplicate shared storage#1555
realrajaryan wants to merge 1 commit into
apple:mainfrom
realrajaryan:fix/system-df-disk-usage

Conversation

@realrajaryan
Copy link
Copy Markdown
Member

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Motivation and Context

This PR fixes system df to report actual on-disk allocated bytes (content blobs + snapshots) instead of summing per-image snapshot sizes. Orphaned blobs are now included as reclaimable, and storage shared across tags is no longer double counted. Also consolidates three identical calculateDirectorySize implementations into a shared FileManager.allocatedSize(of:) extension.

Testing

  • Tested locally
  • Added/updated tests
  • Added/updated docs

@realrajaryan realrajaryan force-pushed the fix/system-df-disk-usage branch from 1dca65d to c00c1cf Compare May 14, 2026 22:22
@github-actions
Copy link
Copy Markdown

Code Coverage

Tier Line Coverage
Unit 34.04%
Integration 20.44%
Combined 53.9%

Copy link
Copy Markdown
Contributor

@jglogan jglogan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think everything looks functionally fine but we shouldn't be accessing the content store directly. The current approach would be broken for any other content store implementation.

import Foundation

extension FileManager {
/// Total bytes allocated on disk for all files in a directory (recursive).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comments should include more details on what is and isn't counted. As far as I can tell:

  • Symlinks to files outside directory will include those files' sizes in the total.
  • Symlinks to files inside directory will include those files' sizes in the total, resulting in inaccuracy due to multiple counting.
  • The same issues apply for hard links.
  • Symlinks to directories aren't followed (which is good).

Do we need to address the double counting issues, or will the directories we enumerate never have these sorts of things?

/// Total bytes allocated on disk for all files in a directory (recursive).
public func allocatedSize(of directory: URL) -> UInt64 {
guard
let enumerator = self.enumerator(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added FileDescriptorOps.enumerate() to ContainerizationOS for TOCTOU-safe traversal of directory trees; it might make sense to use here.

let unpackStrategy = SnapshotStore.defaultUnpackStrategy(initImage: containerSystemConfig.vminit.image)
let snapshotStore = try SnapshotStore(path: root, unpackStrategy: unpackStrategy, log: log)
let service = try ImagesService(contentStore: contentStore, imageStore: imageStore, snapshotStore: snapshotStore, log: log)
let service = try ImagesService(contentStore: contentStore, contentBlobsPath: contentBlobsPath, imageStore: imageStore, snapshotStore: snapshotStore, log: log)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adityaramani should the ImagesService know about the contentBlobsPath? That feels like an encapsulation break.

It's only used for getting the total size of the content store - could we just add a content client call that returns that value?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes +1!

Between container and containerization there are two types are implement the ContentStore protocol

  1. The LocalContentStore from Containerization
  2. The RemoteContentStoreClient from this repo

They exist because there should be only one entity that is actually touching the files on disk - if we had multiple instances of LocalContentStore going about they could trample over each other and mess with the state of the content store.

The RemoteContentStoreClient type talk to the ContentService (also defined in this file) which houses the one singular instance of LocalContentStore

When you ask the RemoteContentStoreClient to ingest something into the content store it proxies that request through to the LocalContentStore which ensures that all operations happen atomically.

With this change - it slightly breaks that abstraction and makes ImageService directly check a path on disk where the files the LocalContentStore store may be.

To solve the "get me the total size of the content store" problem - we can probably do two things.

  1. Add that API to the ContentStore protocol and implement it in both the types
  2. Add a list api to the ContentStore which returns a list of Content https://github.com/apple/containerization/blob/main/Sources/ContainerizationOCI/Content/Content.swift#L23 objects (this is probably a little more involved and needs some deliberation)


extension FileManager {
/// Total bytes allocated on disk for all files in a directory (recursive).
public func allocatedSize(of directory: URL) -> UInt64 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't write any more code that uses URL for fs paths.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also noting that this would be a good candidate for extracting to a FilePathOps utility type in ContainerizationOS: apple/containerization#744.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: container system df omits image content blobs from image size and reclaimable bytes

3 participants