[
  {
    "path": ".github/ISSUE_TEMPLATE/01-bug.yml",
    "content": "name: Bug report\ndescription: File a bug report.\ntitle: \"[Bug]: \"\ntype: \"Bug\"\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for taking the time to fill out this bug report!\n  - type: checkboxes\n    id: prereqs\n    attributes:\n      label: I have done the following\n      description: Select that you have completed the following prerequisites. \n      options:\n        - label: I have searched the existing issues\n          required: true\n        - label: If possible, I've reproduced the issue using the 'main' branch of this project\n          required: false\n  - type: textarea\n    id: reproduce\n    attributes:\n      label: Steps to reproduce\n      description: Explain how to reproduce the incorrect behavior. \n    validations:\n      required: true\n  - type: textarea\n    id: what-happened\n    attributes:\n      label: Current behavior\n      description: A concise description of what you're experiencing.\n    validations:\n      required: true\n  - type: textarea\n    id: expected\n    attributes:\n      label: Expected behavior\n      description: A concise description of what you expected to happen.\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Environment \n      description: |\n        Examples: \n          - **OS**: macOS 26.0 (25A354)\n          - **Xcode**: Version 26.0 (17A324)\n          - **Swift**: Apple Swift version 6.2 (swift-6.2-RELEASE)\n      value: |\n        - OS: \n        - Xcode: \n        - Swift: \n      render: markdown\n    validations:\n      required: true\n  - type: textarea\n    id: logs\n    attributes:\n      label: Relevant log output\n      description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.\n      value: |\n        N/A\n      render: shell\n  - type: checkboxes\n    id: terms\n    attributes:\n      label: Code of Conduct\n      description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/apple/.github/blob/main/CODE_OF_CONDUCT.md).\n      options:\n        - label: I agree to follow this project's Code of Conduct\n          required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/02-feature.yml",
    "content": "name: Feature or enhancement request\ndescription: File a request for a feature or enhancement\ntitle: \"[Request]: \"\ntype: \"Feature\"\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for contributing to the containerization project!\n  - type: textarea\n    id: request\n    attributes:\n      label: Feature or enhancement request details\n      description: Describe your proposed feature or enhancement. Code samples that show what's missing, or what new capabilities will be possible, are very helpful! Provide links to existing issues or external references/discussions, if appropriate.\n    validations:\n      required: true\n  - type: checkboxes\n    id: terms\n    attributes:\n      label: Code of Conduct\n      description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/apple/.github/blob/main/CODE_OF_CONDUCT.md). \n      options:\n        - label: I agree to follow this project's Code of Conduct\n          required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Containerization community support\n    url: https://github.com/apple/container/discussions\n    about: Please ask and answer questions here.\n"
  },
  {
    "path": ".github/workflows/build-test-images.yml",
    "content": "name: Build and publish containerization test images\n\npermissions:\n  contents: read\n\non: \n  workflow_dispatch: \n    inputs: \n      publish: \n        type: boolean\n        description: \"Publish the built image\"\n        default: false\n      version: \n        type: string\n        description: \"Version of the image to create\"\n        default: \"test\"\n      image:\n        type: choice\n        description: Test image to build\n        options:\n          - dockermanifestimage\n          - emptyimage\n        default: 'dockermanifestimage'\n      useBuildx:\n        type: boolean\n        description: \"Use docker buildx to build the image\"\n        default: false\n\njobs: \n  image: \n    name: Build test images\n    timeout-minutes: 30\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      packages: write \n    steps:\n      - name: Check branch\n        env:\n          GH_REF: ${{ github.ref }}\n          PUBLISH: ${{ inputs.publish }}\n        run: |\n          if [[ \"${GH_REF}\" != \"refs/heads/main\" ]] && [[ \"${GH_REF}\" != refs/heads/release* ]] && [[ \"${PUBLISH}\" == \"true\" ]]; then\n            echo \"❌ Cannot publish an image if we are not on main or a release branch.\"\n            exit 1\n          fi\n      - name: Check inputs\n        env:\n          IMAGE: ${{ inputs.image }}\n          USE_BUILDX: ${{ inputs.useBuildx }}\n        run: |\n          if [[ \"${IMAGE}\" == \"dockermanifestimage\" ]] && [[ \"${USE_BUILDX}\" == \"true\" ]]; then\n            echo \"❌ dockermanifestimage cannot be built with buildx\"\n            exit 1\n          fi\n\n          if [[ \"${IMAGE}\" == \"emptyimage\" ]] && [[ \"${USE_BUILDX}\" != \"true\" ]]; then\n            echo \"❌ emptyimage should be built with buildx\"\n            exit 1\n          fi\n      - name: Checkout repository\n        uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8  # v6 \n      - name: Login to GitHub Container Registry\n        uses: docker/login-action@v3\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n      - name: Set up Docker Buildx\n        if: ${{ inputs.useBuildx }} \n        uses: docker/setup-buildx-action@v3\n      - name: Build dockerfile and push image\n        uses: docker/build-push-action@v6\n        with:\n          push: ${{ inputs.publish }}\n          context: Tests/TestImages/${{ inputs.image }}\n          tags: ghcr.io/apple/containerization/${{ inputs.image }}:${{ inputs.version }}\n"
  },
  {
    "path": ".github/workflows/containerization-build-template.yml",
    "content": "name: Build containerization template\n\npermissions:\n  contents: read\n\non: \n  workflow_call:\n    inputs:\n      release:\n        type: boolean\n        description: \"Create a release\"\n        default: false\n      version: \n        type: string\n        description: Version of containerization \n        default: test\n\njobs: \n  buildAndTest: \n    name: Build and Test repo\n    if: github.repository == 'apple/containerization'\n    timeout-minutes: 60\n    runs-on: [self-hosted, macos, tahoe, ARM64]\n    permissions:\n      contents: read\n      packages: write\n    env:\n      DEVELOPER_DIR: \"/Applications/Xcode-latest.app/Contents/Developer\"\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8  # v6\n        with:\n          fetch-depth: 0\n\n      - name: Activate Swiftly\n        run: |\n          source /opt/swiftly/env.sh\n          cat /opt/swiftly/env.sh\n\n      - name: Check formatting\n        run: | \n          ./scripts/install-hawkeye.sh\n          make fmt\n          git diff\n          if ! git diff --quiet ; then echo the following files require formatting or license headers: ; git diff --name-only ; false ; fi\n\n      - name: Check protobufs\n        run: | \n          make protos\n          if ! git diff --quiet ; then echo the following files require formatting or license headers: ; git diff --name-only ; false ; fi\n\n      - name: Make containerization and docs\n        run: |\n          make clean containerization docs\n          tar cfz _site.tgz _site\n        env:\n          BUILD_CONFIGURATION: ${{ inputs.release && 'release' || 'debug' }}\n\n      - name: Make vminitd image\n        run: |\n          source /opt/swiftly/env.sh\n          make -C vminitd swift linux-sdk\n          make init\n        env:\n          BUILD_CONFIGURATION: ${{ inputs.release && 'release' || 'debug' }}\n\n      - name: Test containerization\n        run: | \n          make fetch-default-kernel\n          make test integration\n        env:\n          REGISTRY_TOKEN: ${{ github.token }}\n          REGISTRY_USERNAME: ${{ github.actor }}\n\n      - name: Push vminitd image\n        if: ${{ inputs.release }}\n        env:\n          REGISTRY_TOKEN: ${{ github.token }}\n          REGISTRY_USERNAME: ${{ github.actor }}\n          REGISTRY_HOST: ghcr.io\n          VERSION: ${{ inputs.version }}\n        run: |\n          bin/cctl images tag vminit:latest \"ghcr.io/apple/containerization/vminit:${VERSION}\"\n          bin/cctl images push \"ghcr.io/apple/containerization/vminit:${VERSION}\"\n\n      - name: Create image tar \n        if: ${{ !inputs.release }}\n        run: |\n          bin/cctl images save vminit:latest -o vminit.tar\n\n      - name: Save vminit artifact\n        if: ${{ !inputs.release }}\n        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f  # v6\n        with:\n          name: vminit\n          path: vminit.tar\n\n      - name: Save documentation artifact\n        uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f  # v6\n        with:\n          name: api-docs\n          path: \"./_site.tgz\"\n          retention-days: 14\n\n  uploadPages:\n    # Separate upload step required because upload-pages-artifact needs\n    # gtar which is not on the macOS runner.\n    name: Upload artifact for GitHub Pages\n    needs: buildAndTest\n    timeout-minutes: 5\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Setup Pages\n        uses: actions/configure-pages@v5\n\n      - name: Download a single artifact\n        uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131  # v7\n        with:\n          name: api-docs\n\n      - name: Add API docs to documentation\n        run: |\n          tar xfz _site.tgz\n\n      - name: Upload Artifact\n        uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b  # v4\n        with:\n          path: \"./_site\"\n"
  },
  {
    "path": ".github/workflows/containerization-build.yml",
    "content": "name: Build containerization\n\npermissions:\n  contents: read\n\non:\n  pull_request:\n    types: [opened, reopened, synchronize]\n  push:\n    branches:\n      - main\n      - release/*\n\njobs:\n  verify-signatures:\n    name: Verify commit signatures\n    runs-on: ubuntu-latest\n    if: github.event_name == 'pull_request'\n    steps:\n      - name: Check all commits are signed\n        env:\n          GH_TOKEN: ${{ github.token }}\n          REPO: ${{ github.repository }}\n          PR_NUMBER: ${{ github.event.pull_request.number }}\n        run: |\n          commits=$(gh api \"repos/${REPO}/pulls/${PR_NUMBER}/commits\" --paginate)\n          unsigned_commits=\"\"\n\n          while IFS='|' read -r sha author verified; do\n            if [ \"$verified\" != \"true\" ]; then\n              unsigned_commits=\"$unsigned_commits  - $sha by $author\\n\"\n            fi\n          done < <(echo \"$commits\" | jq -r '.[] | \"\\(.sha)|\\(.commit.author.name)|\\(.commit.verification.verified)\"')\n\n          if [ -n \"$unsigned_commits\" ]; then\n            echo \"::error::The following commits are not signed:\"\n            echo -e \"$unsigned_commits\"\n            echo \"\"\n            echo \"Please sign your commits. See:\"\n            echo  \"  - https://github.com/apple/containerization/blob/main/CONTRIBUTING.md#pull-requests\"\n            echo  \"  - https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits\"\n            exit 1\n          fi\n\n          echo \"All commits are signed!\"\n\n  containerization:\n    permissions:\n      contents: read\n      packages: write\n      pages: write\n    uses: ./.github/workflows/containerization-build-template.yml\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/docs-release.yaml",
    "content": "# Manual workflow for releasing docs ad-hoc. Workflow can only be run for main or release branches. \n# Workflow does NOT publish a release of containerization.\nname: Deploy application website\n\npermissions:\n  contents: read\n\non: \n  workflow_dispatch:\n\njobs: \n  checkBranch:\n    runs-on: ubuntu-latest\n    if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags') || startsWith(github.ref, 'refs/heads/release')\n    steps:\n      - name: Branch validation\n        env:\n          REF_NAME: ${{ github.ref_name }}\n        run: echo \"Branch ${REF_NAME} is allowed\"\n\n  buildSite:\n    name: Build application website\n    needs: checkBranch\n    uses: ./.github/workflows/containerization-build-template.yml\n    secrets: inherit\n    permissions:\n      contents: read\n      packages: write\n      pages: write\n\n  deployDocs:\n    runs-on: ubuntu-latest\n    needs: [checkBranch, buildSite]\n    permissions:\n      contents: read\n      pages: write\n      id-token: write\n\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n\n    steps:\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v4\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release containerization\n\npermissions:\n  contents: read\n\non: \n  push: \n    tags:\n      - \"[0-9]+\\\\.[0-9]+\\\\.[0-9]+\"\n\njobs: \n  containerization:\n    uses: ./.github/workflows/containerization-build-template.yml\n    with: \n      release: true\n      version: ${{ github.ref_name }}\n    secrets: inherit\n    permissions:\n      contents: read\n      packages: write\n      pages: write\n\n  deployDocs:\n    if: startsWith(github.ref, 'refs/tags/')\n    runs-on: ubuntu-latest\n    needs: containerization\n    permissions:\n      contents: read\n      pages: write\n      id-token: write\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    steps:\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v4\n\n  release: \n    if: startsWith(github.ref, 'refs/tags/')\n    name: Publish release\n    timeout-minutes: 30\n    needs: containerization\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n      packages: read\n    steps:\n      - name: Create release\n        uses: softprops/action-gh-release@v2\n        with:\n          token: ${{ github.token }}\n          name: ${{ github.ref_name }}-prerelease\n          draft: true\n          make_latest: false\n          prerelease: true\n          fail_on_unmatched_files: true\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nbin\nlibexec\n.build\n.local\nxcuserdata/\nDerivedData/\n.swiftpm/\n.netrc\n.swiftpm\nworkdir/\ninstaller/\n.venv/\ntest_results/\n*.pid\n*.log\n*.zip\n*.o\n*.ext4\n*.pkg\n*.swp\n*.tar.gz\n*.tar.xz\nvmlinux\n\n# API docs for local preview only.\n_site/\n_serve/\n"
  },
  {
    "path": ".spi.yml",
    "content": "version: 1\nbuilder:\n  configs:\n    - documentation_targets: [Containerization, ContainerizationEXT4, ContainerizationOS, ContainerizationOCI, ContainerizationNetlink, ContainerizationIO, ContainerizationExtras, ContainerizationArchive, SendableProperty]\n      swift_version: '6.2'\n"
  },
  {
    "path": ".swift-format",
    "content": "{\n  \"fileScopedDeclarationPrivacy\" : {\n    \"accessLevel\" : \"private\"\n  },\n  \"indentation\" : {\n    \"spaces\" : 4\n  },\n  \"indentConditionalCompilationBlocks\" : false,\n  \"indentSwitchCaseLabels\" : false,\n  \"lineBreakAroundMultilineExpressionChainComponents\" : false,\n  \"lineBreakBeforeControlFlowKeywords\" : false,\n  \"lineBreakBeforeEachArgument\" : false,\n  \"lineBreakBeforeEachGenericRequirement\" : false,\n  \"lineLength\" : 180,\n  \"maximumBlankLines\" : 1,\n  \"multiElementCollectionTrailingCommas\" : true,\n  \"noAssignmentInExpressions\" : {\n    \"allowedFunctions\" : [\n      \"XCTAssertNoThrow\"\n    ]\n  },\n  \"prioritizeKeepingFunctionOutputTogether\" : false,\n  \"respectsExistingLineBreaks\" : true,\n  \"rules\" : {\n    \"AllPublicDeclarationsHaveDocumentation\" : false,\n    \"AlwaysUseLowerCamelCase\" : true,\n    \"AmbiguousTrailingClosureOverload\" : false,\n    \"BeginDocumentationCommentWithOneLineSummary\" : false,\n    \"DoNotUseSemicolons\" : true,\n    \"DontRepeatTypeInStaticProperties\" : true,\n    \"FileScopedDeclarationPrivacy\" : true,\n    \"FullyIndirectEnum\" : true,\n    \"GroupNumericLiterals\" : true,\n    \"IdentifiersMustBeASCII\" : true,\n    \"NeverForceUnwrap\" : true,\n    \"NeverUseForceTry\" : true,\n    \"NeverUseImplicitlyUnwrappedOptionals\" : true,\n    \"NoAccessLevelOnExtensionDeclaration\" : true,\n    \"NoAssignmentInExpressions\" : true,\n    \"NoBlockComments\" : false,\n    \"NoCasesWithOnlyFallthrough\" : true,\n    \"NoEmptyTrailingClosureParentheses\" : true,\n    \"NoLabelsInCasePatterns\" : true,\n    \"NoLeadingUnderscores\" : false,\n    \"NoParensAroundConditions\" : true,\n    \"NoPlaygroundLiterals\" : true,\n    \"NoVoidReturnOnFunctionSignature\" : true,\n    \"OmitExplicitReturns\" : true,\n    \"OneCasePerLine\" : true,\n    \"OneVariableDeclarationPerLine\" : true,\n    \"OnlyOneTrailingClosureArgument\" : true,\n    \"OrderedImports\" : true,\n    \"ReplaceForEachWithForLoop\" : true,\n    \"ReturnVoidInsteadOfEmptyTuple\" : true,\n    \"TypeNamesShouldBeCapitalized\" : true,\n    \"UseEarlyExits\" : true,\n    \"UseLetInEveryBoundCaseVariable\" : true,\n    \"UseShorthandTypeNames\" : true,\n    \"UseSingleLinePropertyGetter\" : true,\n    \"UseSynthesizedInitializer\" : true,\n    \"UseTripleSlashForDocumentationComments\" : true,\n    \"UseWhereClausesInForLoops\" : false,\n    \"ValidateDocumentationComments\" : true\n  },\n  \"spacesAroundRangeFormationOperators\" : false,\n  \"tabWidth\" : 2,\n  \"version\" : 1\n}\n"
  },
  {
    "path": ".swift-format-nolint",
    "content": "{\n  \"fileScopedDeclarationPrivacy\" : {\n    \"accessLevel\" : \"private\"\n  },\n  \"indentation\" : {\n    \"spaces\" : 4\n  },\n  \"indentConditionalCompilationBlocks\" : false,\n  \"indentSwitchCaseLabels\" : false,\n  \"lineBreakAroundMultilineExpressionChainComponents\" : false,\n  \"lineBreakBeforeControlFlowKeywords\" : false,\n  \"lineBreakBeforeEachArgument\" : false,\n  \"lineBreakBeforeEachGenericRequirement\" : false,\n  \"lineLength\" : 180,\n  \"maximumBlankLines\" : 1,\n  \"multiElementCollectionTrailingCommas\" : true,\n  \"noAssignmentInExpressions\" : {\n    \"allowedFunctions\" : [\n      \"XCTAssertNoThrow\"\n    ]\n  },\n  \"prioritizeKeepingFunctionOutputTogether\" : false,\n  \"respectsExistingLineBreaks\" : true,\n  \"rules\" : {\n    \"AllPublicDeclarationsHaveDocumentation\" : false,\n    \"AlwaysUseLowerCamelCase\" : false,\n    \"AmbiguousTrailingClosureOverload\" : false,\n    \"BeginDocumentationCommentWithOneLineSummary\" : false,\n    \"DoNotUseSemicolons\" : true,\n    \"DontRepeatTypeInStaticProperties\" : false,\n    \"FileScopedDeclarationPrivacy\" : false,\n    \"FullyIndirectEnum\" : false,\n    \"GroupNumericLiterals\" : false,\n    \"IdentifiersMustBeASCII\" : false,\n    \"NeverForceUnwrap\" : false,\n    \"NeverUseForceTry\" : false,\n    \"NeverUseImplicitlyUnwrappedOptionals\" : false,\n    \"NoAccessLevelOnExtensionDeclaration\" : false,\n    \"NoAssignmentInExpressions\" : false,\n    \"NoBlockComments\" : false,\n    \"NoCasesWithOnlyFallthrough\" : false,\n    \"NoEmptyTrailingClosureParentheses\" : true,\n    \"NoLabelsInCasePatterns\" : false,\n    \"NoLeadingUnderscores\" : false,\n    \"NoParensAroundConditions\" : true,\n    \"NoPlaygroundLiterals\" : false,\n    \"NoVoidReturnOnFunctionSignature\" : true,\n    \"OmitExplicitReturns\" : false,\n    \"OneCasePerLine\" : true,\n    \"OneVariableDeclarationPerLine\" : true,\n    \"OnlyOneTrailingClosureArgument\" : false,\n    \"OrderedImports\" : true,\n    \"ReplaceForEachWithForLoop\" : false,\n    \"ReturnVoidInsteadOfEmptyTuple\" : false,\n    \"TypeNamesShouldBeCapitalized\" : false,\n    \"UseEarlyExits\" : false,\n    \"UseLetInEveryBoundCaseVariable\" : false,\n    \"UseShorthandTypeNames\" : true,\n    \"UseSingleLinePropertyGetter\" : true,\n    \"UseSynthesizedInitializer\" : false,\n    \"UseTripleSlashForDocumentationComments\" : true,\n    \"UseWhereClausesInForLoops\" : false,\n    \"ValidateDocumentationComments\" : false\n  },\n  \"spacesAroundRangeFormationOperators\" : false,\n  \"tabWidth\" : 2,\n  \"version\" : 1\n}\n"
  },
  {
    "path": ".swift-version",
    "content": "6.3-snapshot-2026-02-27"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# 🌈 📦️ Welcome to the Containerization community! 📦️ 🌈\n\nContributions to Containerization are welcomed and encouraged.\n\n## Index\n\n- [How you can help](#how-you-can-help)\n- [Submitting issues and pull requests](#submitting-issues-and-pull-requests)\n- [New to open source?](#new-to-open-source)\n- [AI contribution guidelines](#ai-contribution-guidelines)\n- [Code of conduct](#code-of-conduct)\n\n## How you can help\n\nWe would love your contributions in the form of:\n\n🐛 Bug fixes\\\n⚡️ Performance improvements\\\n✨ API additions or enhancements\\\n📝 Documentation\\\n🧑‍💻 Project advocacy: blogs, conference talks, and more\n\nAnything else that could enhance the project!\n\n## Submitting issues and pull requests\n\n### Issues\n\nTo file a bug or feature request, use [GitHub issues](https://github.com/apple/containerization/issues/new).\n\n🚧 For unexpected behavior or usability limitations, detailed instructions on how to reproduce the issue are appreciated. This will greatly help the priority setting and speed at which maintainers can get to your issue.\n\n### Pull requests\n\nWe require all commits be signed with any of GitHub's supported methods, such as GPG or SSH. Information on how to set this up can be found on [GitHub's docs](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification#about-commit-signature-verification).\n\nTo make a pull request, use [GitHub](https://github.com/apple/containerization/compare). Please give the team a few days to review but it's ok to check in on occasion. We appreciate your contribution!\n\n> [!IMPORTANT]\n> If you plan to make substantial changes or add new features, we encourage you to first discuss them with the wider containerization developer community.\n> You can do this by filing a [GitHub issue](https://github.com/apple/containerization/issues/new).\n> This will save time and increases the chance of your pull request being accepted.\n\nWe use a \"squash and merge\" strategy to keep our `main` branch history clean and easy to follow. When your pull request\nis merged, all of your commits will be combined into a single commit.\n\nWith the \"squash and merge\" strategy, the *title* and *body* of your pull request is extremely important. It will become the commit message\nfor the squashed commit. Think of it as the single, definitive description of your contribution.\n\nBefore merging, we'll review the pull request title and body to ensure it:\n\n* Clearly and concisely describes the changes.\n* Uses the imperative mood (for example, \"Add feature,\" \"Fix bug\").\n* Provides enough context for future developers to understand the purpose of the change.\n\nThe pull request description should be concise and accurately describe the *what* and *why* of your changes.\n\n#### .gitignore contributions\n\nWe do not currently accept contributions to add editor specific additions to the root .gitignore. We urge contributors to make a global .gitignore file with their rulesets they may want to add instead. A global .gitignore file can be set like so:\n\n```bash\ngit config --global core.excludesfile ~/.gitignore\n```\n\n#### Formatting contributions\n\nMake sure your contributions are consistent with the rest of the project's formatting. You can do this using our Makefile:\n\n```bash\nmake fmt\n```\n\n#### Applying license header to new files\n\nIf you submit a contribution that adds a new file, please add the license header. You can do this using our Makefile:\n\n```bash\nmake update-licenses\n```\n\n## New to open source?\n\n### How do I pick something to work on?\n\nTake a look at the `good first issue` label in the [containerization](https://github.com/apple/containerization/contribute) or [container](https://github.com/apple/container/contribute) project. \nBefore you start working on an issue:\n* Check the comments, assignees, and any references to pull requests — make sure nobody else is actively working on it, or awaiting help or review.\n* If someone is assigned to the issue or volunteered to work on it, and there are no signs of progress or activity over at least the past month, don't hesitate to check in with them\n* Leave a comment that you have started working on it.\n\n### Getting help\n\nDon't be afraid to ask for help! When asking for help, provide as much information as possible, while highlighting anything you think may be important. Refer to the [MAINTAINERS.txt](MAINTAINERS.txt) file for the appropriate people to ping.\n\n### I didn't get a response from someone. What should I do?\n\nIt's possible that you ask someone a question in an issue/pull request and you don't get a response as quickly as you'd like. If you don't get a response within a week, it's okay to politely ping them using an `@` mention. If you don't get a response for 2-3 weeks in a row, please ping someone else.\n\n### I can't finish the contribution I started. \n\nSometimes an issue ends up bigger, harder, or more time-consuming than expected — **and that’s completely fine.** Be sure to comment on the issue saying you’re stepping away, so that someone else is able to pick it up.\n\n## AI contribution guidelines\n\nWe welcome thoughtful use of AI tools in your contributions to this repository. We ask that you adhere to these rules in order to preserve the project's integrity, clarity, and quality, and to respect maintainer bandwidth:\n\n* You should be able to explain and justify every line of code or documentation that was generated or assisted by AI. Your submission should reflect your own understanding and intent.\n* Use AI to augment, not totally replace, your reasoning or familiarity, especially for non-trivial parts of the system. \n* Avoid dumping AI-generated walls of text that you cannot explain. Low-effort, unexplained submissions will be deprioritized to protect maintainer bandwidth.\n\nAI tools should be used to **enhance, not replace** the human elements that make OSS special: learning, collaboration, and community growth.\n\n## Code of conduct\n\nTo clarify of what is expected of our contributors and community members, the Containerization team has adopted the code of conduct defined by the Contributor Covenant. This document is used across many open source communities and articulates our values well. For more detail, please read the [Code of Conduct](https://github.com/apple/.github/blob/main/CODE_OF_CONDUCT.md \"Code of Conduct\").\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "MAINTAINERS.txt",
    "content": "This file contains a list of maintainers and past maintainers who have made meaningful changes to this repository.\n\n### Maintainers\n\nAditya Ramani (adityaramani)\nAJ Emory (ajemory)\nDanny Canter (dcantah)\nDmitry Kovba (dkovba)\nEric Ernst (egernst)\nJohn Logan (jglogan)\nKathryn Baldauf (katiewasnothere)\nMadhu Venugopal (mavenugo)\nMichael Crosby (crosbymichael)\nRaj Aryan Singh (realrajaryan)\nSidhartha Mani (wlan0)\nYibo Zhuang (yibozhuang)\n\n\n### Emeritus maintainers\n\nAgam Dua (agamdua)\nEvan Hazlett (ehazlett)\nGilbert Song (gilbert88)\nHugh Bussell (hughbussell)\nTanweer Noor (tanweernoor)\nXimena Perez Diaz (ximenanperez)\n"
  },
  {
    "path": "Makefile",
    "content": "# Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#   https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Build configuration variables\nBUILD_CONFIGURATION ?= debug\nWARNINGS_AS_ERRORS ?= true\nSWIFT_CONFIGURATION := $(if $(filter-out false,$(WARNINGS_AS_ERRORS)),-Xswiftc -warnings-as-errors) --disable-automatic-resolution\n\n# Commonly used locations\nSWIFT := \"/usr/bin/swift\"\nROOT_DIR := $(shell git rev-parse --show-toplevel)\nBUILD_BIN_DIR = $(shell $(SWIFT) build -c $(BUILD_CONFIGURATION) --show-bin-path)\nCOV_DATA_DIR = $(shell $(SWIFT) test --show-coverage-path | xargs dirname)\nCOV_REPORT_FILE = $(ROOT_DIR)/code-coverage-report\n\n# Variables for libarchive integration\nLIBARCHIVE_UPSTREAM_REPO := https://github.com/libarchive/libarchive\nLIBARCHIVE_UPSTREAM_VERSION := v3.7.7\nLIBARCHIVE_LOCAL_DIR := workdir/libarchive\n\nKATA_BINARY_PACKAGE := https://github.com/kata-containers/kata-containers/releases/download/3.17.0/kata-static-3.17.0-arm64.tar.xz\n\ninclude Protobuf.Makefile\n.DEFAULT_GOAL := all\n\n.PHONY: all\nall: containerization\nall: init\n\n.PHONY: release\nrelease: BUILD_CONFIGURATION = release\nrelease: all\n\n.PHONY: containerization\ncontainerization:\n\t@echo Building containerization binaries...\n\t@$(SWIFT) --version\n\t@$(SWIFT) build -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION)\n\n\t@echo Copying containerization binaries...\n\t@mkdir -p bin\n\t@install \"$(BUILD_BIN_DIR)/cctl\" ./bin/\n\t@install \"$(BUILD_BIN_DIR)/containerization-integration\" ./bin/\n\n\t@echo Signing containerization binaries...\n\t@codesign --force --sign - --timestamp=none --entitlements=signing/vz.entitlements bin/cctl\n\t@codesign --force --sign - --timestamp=none --entitlements=signing/vz.entitlements bin/containerization-integration\n\n.PHONY: init\ninit: containerization vminitd\n\t@echo Creating init.ext4...\n\t@rm -f bin/init.rootfs.tar.gz bin/init.block\n\t@./bin/cctl rootfs create \\\n\t\t--vminitd vminitd/bin/vminitd \\\n\t\t--vmexec vminitd/bin/vmexec \\\n\t\t--label org.opencontainers.image.source=https://github.com/apple/containerization \\\n\t\t--image vminit:latest \\\n\t\tbin/init.rootfs.tar.gz\n\n.PHONY: cross-prep\ncross-prep:\n\t@\"$(MAKE)\" -C vminitd cross-prep\n\n.PHONY: vminitd\nvminitd:\n\t@mkdir -p ./bin\n\t@\"$(MAKE)\" -C vminitd BUILD_CONFIGURATION=$(BUILD_CONFIGURATION) WARNINGS_AS_ERRORS=$(WARNINGS_AS_ERRORS)\n\n.PHONY: update-libarchive-source\nupdate-libarchive-source:\n\t@echo Updating the libarchive source files...\n\t@git clone $(LIBARCHIVE_UPSTREAM_REPO) --depth 1 --branch $(LIBARCHIVE_UPSTREAM_VERSION) \"$(LIBARCHIVE_LOCAL_DIR)\"\n\t@cp \"$(LIBARCHIVE_LOCAL_DIR)/libarchive/archive_entry.h\" Sources/ContainerizationArchive/CArchive/include\n\t@cp \"$(LIBARCHIVE_LOCAL_DIR)/libarchive/archive.h\" Sources/ContainerizationArchive/CArchive/include\n\t@cp \"$(LIBARCHIVE_LOCAL_DIR)/COPYING\" Sources/ContainerizationArchive/CArchive/COPYING\n\t@rm -rf \"$(LIBARCHIVE_LOCAL_DIR)\"\n\n.PHONY: test\ntest:\n\t@echo Testing all test targets...\n\t@$(SWIFT) test --enable-code-coverage $(SWIFT_CONFIGURATION)\n\n.PHONY: coverage\ncoverage: test\n\t@echo Generating code coverage report...\n\t@xcrun llvm-cov show --compilation-dir=`pwd` \\\n\t\t-instr-profile=$(COV_DATA_DIR)/default.profdata \\\n\t\t--ignore-filename-regex=\".build/\" \\\n\t\t--ignore-filename-regex=\".pb.swift\" \\\n\t\t--ignore-filename-regex=\".proto\" \\\n\t\t--ignore-filename-regex=\".grpc.swift\" \\\n\t\t$(BUILD_BIN_DIR)/containerizationPackageTests.xctest/Contents/MacOS/containerizationPackageTests > $(COV_REPORT_FILE)\n\t@echo Code coverage report generated: $(COV_REPORT_FILE)\n\n.PHONY: integration\nintegration:\nifeq (,$(wildcard bin/vmlinux))\n\t@echo No bin/vmlinux kernel found. See fetch-default-kernel target.\n\t@exit 1\nendif\n\t@echo Running the integration tests...\n\t@./bin/containerization-integration\n\n.PHONY: fetch-default-kernel\nfetch-default-kernel:\n\t@mkdir -p .local/ bin/\nifeq (,$(wildcard .local/kata.tar.gz))\n\t@curl -SsL -o .local/kata.tar.gz ${KATA_BINARY_PACKAGE}\nendif\nifeq (,$(wildcard .local/vmlinux))\n\t@tar -zxf .local/kata.tar.gz -C .local/ --strip-components=1\n\t@cp -L .local/opt/kata/share/kata-containers/vmlinux.container .local/vmlinux\nendif\nifeq (,$(wildcard bin/vmlinux))\n\t@cp .local/vmlinux bin/vmlinux\nendif\n\n.PHONY: check\ncheck: swift-fmt-check check-licenses\n\n.PHONY: fmt\nfmt: swift-fmt update-licenses\n\n.PHONY: swift-fmt\nSWIFT_SRC = $(shell find . -type f -name '*.swift' -not -path \"*/.*\" -not -path \"*.pb.swift\" -not -path \"*.grpc.swift\" -not -path \"*/checkouts/*\")\nswift-fmt:\n\t@echo Applying the standard code formatting...\n\t@$(SWIFT) format --recursive --configuration .swift-format -i $(SWIFT_SRC)\n\nswift-fmt-check:\n\t   @echo Applying the standard code formatting...\n\t   @$(SWIFT) format lint --recursive --strict --configuration .swift-format-nolint $(SWIFT_SRC)\n\n.PHONY: update-licenses\nupdate-licenses:\n\t@echo Updating license headers...\n\t@./scripts/ensure-hawkeye-exists.sh\n\t@.local/bin/hawkeye format --fail-if-unknown --fail-if-updated false\n\n.PHONY: check-licenses\ncheck-licenses:\n\t@echo Checking license headers existence in source files...\n\t@./scripts/ensure-hawkeye-exists.sh\n\t@.local/bin/hawkeye check --fail-if-unknown\n\n.PHONY: pre-commit\npre-commit:\n\t   cp Scripts/pre-commit.fmt .git/hooks\n\t   touch .git/hooks/pre-commit\n\t   cat .git/hooks/pre-commit | grep -v 'hooks/pre-commit\\.fmt' > /tmp/pre-commit.new || true\n\t   echo 'PRECOMMIT_NOFMT=$${PRECOMMIT_NOFMT} $$(git rev-parse --show-toplevel)/.git/hooks/pre-commit.fmt' >> /tmp/pre-commit.new\n\t   mv /tmp/pre-commit.new .git/hooks/pre-commit\n\t   chmod +x .git/hooks/pre-commit\n\n.PHONY: serve-docs\nserve-docs:\n\t@echo 'to browse: open http://127.0.0.1:8000/containerization/documentation/'\n\t@rm -rf _serve\n\t@mkdir -p _serve\n\t@cp -a _site _serve/containerization\n\t@python3 -m http.server --bind 127.0.0.1 --directory ./_serve\n\n.PHONY: docs\ndocs:\n\t@echo Updating API documentation...\n\t@rm -rf _site\n\t@scripts/make-docs.sh _site containerization\n\n.PHONY: cleancontent\ncleancontent:\n\t@echo Cleaning the content...\n\t@rm -rf ~/Library/Application\\ Support/com.apple.containerization\n\n.PHONY: clean\nclean:\n\t@echo Cleaning build files...\n\t@rm -rf bin/\n\t@rm -rf _site/\n\t@rm -rf _serve/\n\t@rm -f $(COV_REPORT_FILE)\n\t@$(SWIFT) package clean\n\t@\"$(MAKE)\" -C vminitd clean\n"
  },
  {
    "path": "Package.resolved",
    "content": "{\n  \"originHash\" : \"8b51a9ec068537ab57ce9b8034b5b84a02a4697e4a6be491954e5fbda7e5783b\",\n  \"pins\" : [\n    {\n      \"identity\" : \"async-http-client\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swift-server/async-http-client.git\",\n      \"state\" : {\n        \"revision\" : \"60235983163d040f343a489f7e2e77c1918a8bd9\",\n        \"version\" : \"1.26.1\"\n      }\n    },\n    {\n      \"identity\" : \"grpc-swift\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/grpc/grpc-swift.git\",\n      \"state\" : {\n        \"revision\" : \"a56a157218877ef3e9625f7e1f7b2cb7e46ead1b\",\n        \"version\" : \"1.26.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-algorithms\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-algorithms.git\",\n      \"state\" : {\n        \"revision\" : \"87e50f483c54e6efd60e885f7f5aa946cee68023\",\n        \"version\" : \"1.2.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-argument-parser\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-argument-parser.git\",\n      \"state\" : {\n        \"revision\" : \"011f0c765fb46d9cac61bca19be0527e99c98c8b\",\n        \"version\" : \"1.5.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-asn1\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-asn1.git\",\n      \"state\" : {\n        \"revision\" : \"a54383ada6cecde007d374f58f864e29370ba5c3\",\n        \"version\" : \"1.3.2\"\n      }\n    },\n    {\n      \"identity\" : \"swift-async-algorithms\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-async-algorithms.git\",\n      \"state\" : {\n        \"revision\" : \"042e1c4d9d19748c9c228f8d4ebc97bb1e339b0b\",\n        \"version\" : \"1.0.4\"\n      }\n    },\n    {\n      \"identity\" : \"swift-atomics\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-atomics.git\",\n      \"state\" : {\n        \"revision\" : \"cd142fd2f64be2100422d658e7411e39489da985\",\n        \"version\" : \"1.2.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-certificates\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-certificates.git\",\n      \"state\" : {\n        \"revision\" : \"999fd70c7803da89f3904d635a6815a2a7cd7585\",\n        \"version\" : \"1.10.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-collections\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-collections.git\",\n      \"state\" : {\n        \"revision\" : \"c1805596154bb3a265fd91b8ac0c4433b4348fb0\",\n        \"version\" : \"1.2.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-crypto\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-crypto.git\",\n      \"state\" : {\n        \"revision\" : \"e8d6eba1fef23ae5b359c46b03f7d94be2f41fed\",\n        \"version\" : \"3.12.3\"\n      }\n    },\n    {\n      \"identity\" : \"swift-docc-plugin\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swiftlang/swift-docc-plugin\",\n      \"state\" : {\n        \"revision\" : \"d1691545d53581400b1de9b0472d45eb25c19fed\",\n        \"version\" : \"1.4.4\"\n      }\n    },\n    {\n      \"identity\" : \"swift-docc-symbolkit\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swiftlang/swift-docc-symbolkit\",\n      \"state\" : {\n        \"revision\" : \"b45d1f2ed151d057b54504d653e0da5552844e34\",\n        \"version\" : \"1.0.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-http-structured-headers\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-http-structured-headers.git\",\n      \"state\" : {\n        \"revision\" : \"db6eea3692638a65e2124990155cd220c2915903\",\n        \"version\" : \"1.3.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-http-types\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-http-types.git\",\n      \"state\" : {\n        \"revision\" : \"a0a57e949a8903563aba4615869310c0ebf14c03\",\n        \"version\" : \"1.4.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-log\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-log.git\",\n      \"state\" : {\n        \"revision\" : \"3d8596ed08bd13520157f0355e35caed215ffbfa\",\n        \"version\" : \"1.6.3\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio.git\",\n      \"state\" : {\n        \"revision\" : \"34d486b01cd891297ac615e40d5999536a1e138d\",\n        \"version\" : \"2.83.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-extras\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-extras.git\",\n      \"state\" : {\n        \"revision\" : \"145db1962f4f33a4ea07a32e751d5217602eea29\",\n        \"version\" : \"1.28.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-http2\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-http2.git\",\n      \"state\" : {\n        \"revision\" : \"4281466512f63d1bd530e33f4aa6993ee7864be0\",\n        \"version\" : \"1.36.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-ssl\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-ssl.git\",\n      \"state\" : {\n        \"revision\" : \"173cc69a058623525a58ae6710e2f5727c663793\",\n        \"version\" : \"2.36.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-transport-services\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-transport-services.git\",\n      \"state\" : {\n        \"revision\" : \"cd1e89816d345d2523b11c55654570acd5cd4c56\",\n        \"version\" : \"1.24.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-numerics\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-numerics.git\",\n      \"state\" : {\n        \"revision\" : \"e0ec0f5f3af6f3e4d5e7a19d2af26b481acb6ba8\",\n        \"version\" : \"1.0.3\"\n      }\n    },\n    {\n      \"identity\" : \"swift-protobuf\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-protobuf.git\",\n      \"state\" : {\n        \"revision\" : \"102a647b573f60f73afdce5613a51d71349fe507\",\n        \"version\" : \"1.30.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-service-lifecycle\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swift-server/swift-service-lifecycle.git\",\n      \"state\" : {\n        \"revision\" : \"e7187309187695115033536e8fc9b2eb87fd956d\",\n        \"version\" : \"2.8.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-system\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-system.git\",\n      \"state\" : {\n        \"revision\" : \"395a77f0aa927f0ff73941d7ac35f2b46d47c9db\",\n        \"version\" : \"1.6.3\"\n      }\n    },\n    {\n      \"identity\" : \"zstd\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/facebook/zstd.git\",\n      \"state\" : {\n        \"revision\" : \"f8745da6ff1ad1e7bab384bd1f9d742439278e99\",\n        \"version\" : \"1.5.7\"\n      }\n    }\n  ],\n  \"version\" : 3\n}\n"
  },
  {
    "path": "Package.swift",
    "content": "// swift-tools-version: 6.2\n//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n// The swift-tools-version declares the minimum version of Swift required to build this package.\n\nimport CompilerPluginSupport\nimport Foundation\nimport PackageDescription\n\nlet package = Package(\n    name: \"containerization\",\n    platforms: [.macOS(\"15\")],\n    products: [\n        .library(name: \"Containerization\", targets: [\"Containerization\", \"ContainerizationError\"]),\n        .library(name: \"ContainerizationEXT4\", targets: [\"ContainerizationEXT4\"]),\n        .library(name: \"ContainerizationOCI\", targets: [\"ContainerizationOCI\"]),\n        .library(name: \"ContainerizationNetlink\", targets: [\"ContainerizationNetlink\"]),\n        .library(name: \"ContainerizationIO\", targets: [\"ContainerizationIO\"]),\n        .library(name: \"ContainerizationOS\", targets: [\"ContainerizationOS\"]),\n        .library(name: \"ContainerizationExtras\", targets: [\"ContainerizationExtras\"]),\n        .library(name: \"ContainerizationArchive\", targets: [\"ContainerizationArchive\"]),\n        .executable(name: \"cctl\", targets: [\"cctl\"]),\n    ],\n    dependencies: [\n        .package(url: \"https://github.com/apple/swift-log.git\", from: \"1.0.0\"),\n        .package(url: \"https://github.com/apple/swift-argument-parser.git\", from: \"1.3.0\"),\n        .package(url: \"https://github.com/apple/swift-collections.git\", from: \"1.1.4\"),\n        .package(url: \"https://github.com/apple/swift-crypto.git\", from: \"3.0.0\"),\n        .package(url: \"https://github.com/grpc/grpc-swift.git\", from: \"1.26.0\"),\n        .package(url: \"https://github.com/apple/swift-protobuf.git\", from: \"1.29.0\"),\n        .package(url: \"https://github.com/apple/swift-nio.git\", from: \"2.80.0\"),\n        .package(url: \"https://github.com/swift-server/async-http-client.git\", from: \"1.20.1\"),\n        .package(url: \"https://github.com/apple/swift-system.git\", from: \"1.4.0\"),\n        .package(url: \"https://github.com/swiftlang/swift-docc-plugin\", from: \"1.1.0\"),\n        .package(url: \"https://github.com/apple/swift-nio-ssl.git\", from: \"2.36.0\"),\n        .package(url: \"https://github.com/facebook/zstd.git\", exact: \"1.5.7\"),\n    ],\n    targets: [\n        .target(\n            name: \"ContainerizationError\"\n        ),\n        .target(\n            name: \"Containerization\",\n            dependencies: [\n                .product(name: \"Logging\", package: \"swift-log\"),\n                .product(name: \"GRPC\", package: \"grpc-swift\"),\n                .product(name: \"SystemPackage\", package: \"swift-system\"),\n                .product(name: \"_NIOFileSystem\", package: \"swift-nio\"),\n                \"ContainerizationArchive\",\n                \"ContainerizationOCI\",\n                \"ContainerizationOS\",\n                \"ContainerizationIO\",\n                \"ContainerizationExtras\",\n                .target(name: \"ContainerizationEXT4\", condition: .when(platforms: [.macOS])),\n            ],\n            exclude: [\n                \"../Containerization/SandboxContext/SandboxContext.proto\"\n            ]\n        ),\n        .executableTarget(\n            name: \"cctl\",\n            dependencies: [\n                .product(name: \"Logging\", package: \"swift-log\"),\n                .product(name: \"ArgumentParser\", package: \"swift-argument-parser\"),\n                \"Containerization\",\n                \"ContainerizationArchive\",\n                \"ContainerizationEXT4\",\n                \"ContainerizationExtras\",\n                \"ContainerizationOCI\",\n                \"ContainerizationOS\",\n            ]\n        ),\n        .executableTarget(\n            name: \"containerization-integration\",\n            dependencies: [\n                .product(name: \"Logging\", package: \"swift-log\"),\n                .product(name: \"ArgumentParser\", package: \"swift-argument-parser\"),\n                \"Containerization\",\n            ],\n            path: \"Sources/Integration\"\n        ),\n        .testTarget(\n            name: \"ContainerizationUnitTests\",\n            dependencies: [\"Containerization\"],\n            path: \"Tests/ContainerizationTests\",\n            resources: [\n                .copy(\"ImageTests/Resources/scratch.tar\"),\n                .copy(\"ImageTests/Resources/scratch_no_annotations.tar\"),\n            ]\n        ),\n        .target(\n            name: \"ContainerizationEXT4\",\n            dependencies: [\n                .target(name: \"ContainerizationArchive\", condition: .when(platforms: [.macOS])),\n                .product(name: \"SystemPackage\", package: \"swift-system\"),\n                \"ContainerizationOS\",\n            ],\n            path: \"Sources/ContainerizationEXT4\",\n            exclude: [\n                \"README.md\"\n            ]\n        ),\n        .testTarget(\n            name: \"ContainerizationEXT4Tests\",\n            dependencies: [\n                \"ContainerizationEXT4\",\n                \"ContainerizationArchive\",\n            ],\n            resources: [\n                .copy(\n                    \"Resources/content/blobs/sha256/ad59e9f71edceca7b1ac7c642410858489b743c97233b0a26a5e2098b1443762\"),  // index\n                .copy(\n                    \"Resources/content/blobs/sha256/48a06049d3738991b011ca8b12473d712b7c40666a1462118dae3c403676afc2\"),  // manifest\n                .copy(\n                    \"Resources/content/blobs/sha256/8e2eb240a6cd7be1a0d308125afe0060b020e89275ced2e729eda7d4eeff62a2\"),  // config\n                .copy(\n                    \"Resources/content/blobs/sha256/c6b39de5b33961661dc939b997cc1d30cda01e38005a6c6625fd9c7e748bab44\"),  // layer 1\n                .copy(\n                    \"Resources/content/blobs/sha256/4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1\"),  // layer 2\n            ]\n        ),\n        .target(\n            name: \"ContainerizationArchive\",\n            dependencies: [\n                .product(name: \"SystemPackage\", package: \"swift-system\"),\n                \"CArchive\",\n                \"ContainerizationExtras\",\n                \"ContainerizationOS\",\n            ],\n            exclude: [\n                \"CArchive\"\n            ]\n        ),\n        .testTarget(\n            name: \"ContainerizationArchiveTests\",\n            dependencies: [\n                \"ContainerizationArchive\"\n            ],\n            resources: [\n                .copy(\"Resources/test.tar.zst\")\n            ]\n        ),\n        .target(\n            name: \"CArchive\",\n            dependencies: [\n                .product(name: \"libzstd\", package: \"zstd\")\n            ],\n            path: \"Sources/ContainerizationArchive/CArchive\",\n            sources: [\n                \"archive_swift_bridge.c\"\n            ],\n            cSettings: [\n                .define(\n                    \"PLATFORM_CONFIG_H\", to: \"\\\"config_darwin.h\\\"\",\n                    .when(platforms: [.iOS, .macOS, .macCatalyst, .watchOS, .driverKit, .tvOS])),\n                .define(\"PLATFORM_CONFIG_H\", to: \"\\\"config_linux.h\\\"\", .when(platforms: [.linux])),\n                .unsafeFlags([\"-fno-modules\"]),\n            ],\n            linkerSettings: [\n                .linkedLibrary(\"z\"),\n                .linkedLibrary(\"bz2\"),\n                .linkedLibrary(\"lzma\"),\n                .linkedLibrary(\"archive\"),\n                .linkedLibrary(\"iconv\", .when(platforms: [.macOS])),\n                .linkedLibrary(\"crypto\", .when(platforms: [.linux])),\n            ]\n        ),\n        .target(\n            name: \"ContainerizationOCI\",\n            dependencies: [\n                .product(name: \"AsyncHTTPClient\", package: \"async-http-client\"),\n                .product(name: \"Crypto\", package: \"swift-crypto\"),\n                .product(name: \"Logging\", package: \"swift-log\"),\n                .product(name: \"_NIOFileSystem\", package: \"swift-nio\"),\n                \"ContainerizationError\",\n                \"ContainerizationOS\",\n                \"ContainerizationExtras\",\n            ]\n        ),\n        .testTarget(\n            name: \"ContainerizationOCITests\",\n            dependencies: [\n                \"ContainerizationOCI\",\n                \"Containerization\",\n                \"ContainerizationIO\",\n                .product(name: \"NIO\", package: \"swift-nio\"),\n                .product(name: \"Crypto\", package: \"swift-crypto\"),\n            ]\n        ),\n        .target(\n            name: \"ContainerizationNetlink\",\n            dependencies: [\n                .product(name: \"Logging\", package: \"swift-log\"),\n                \"ContainerizationOS\",\n                \"ContainerizationExtras\",\n            ]\n        ),\n        .testTarget(\n            name: \"ContainerizationNetlinkTests\",\n            dependencies: [\n                \"ContainerizationNetlink\"\n            ]\n        ),\n        .target(\n            name: \"ContainerizationOS\",\n            dependencies: [\n                .product(name: \"Logging\", package: \"swift-log\"),\n                .product(name: \"SystemPackage\", package: \"swift-system\"),\n                \"CShim\",\n                \"ContainerizationError\",\n            ],\n            exclude: [\n                \"../ContainerizationOS/README.md\"\n            ]\n        ),\n        .testTarget(\n            name: \"ContainerizationOSTests\",\n            dependencies: [\n                .product(name: \"SystemPackage\", package: \"swift-system\"),\n                \"ContainerizationOS\",\n                \"ContainerizationExtras\",\n            ]\n        ),\n        .target(\n            name: \"ContainerizationIO\",\n            dependencies: [\n                \"ContainerizationOS\",\n                .product(name: \"NIO\", package: \"swift-nio\"),\n                .product(name: \"NIOCore\", package: \"swift-nio\"),\n                .product(name: \"NIOFoundationCompat\", package: \"swift-nio\"),\n            ]\n        ),\n        .target(\n            name: \"ContainerizationExtras\",\n            dependencies: [\n                \"ContainerizationError\",\n                .product(name: \"Collections\", package: \"swift-collections\"),\n                .product(name: \"Logging\", package: \"swift-log\"),\n                .product(name: \"NIOSSL\", package: \"swift-nio-ssl\"),\n\n            ]\n        ),\n        .testTarget(\n            name: \"ContainerizationExtrasTests\",\n            dependencies: [\n                \"ContainerizationExtras\",\n                \"CShim\",\n            ]\n        ),\n        .target(\n            name: \"CShim\"\n        ),\n    ]\n)\n"
  },
  {
    "path": "Protobuf.Makefile",
    "content": "# Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#   https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nLOCAL_DIR := $(ROOT_DIR)/.local\nLOCAL_BIN_DIR := $(LOCAL_DIR)/bin\n\n# Versions\nPROTOC_VERSION := 26.1\n\n# Protoc binary installation\nPROTOC_ZIP := protoc-$(PROTOC_VERSION)-osx-universal_binary.zip\nPROTOC := $(LOCAL_BIN_DIR)/protoc@$(PROTOC_VERSION)/protoc\n$(PROTOC):\n\t@echo Downloading protocol buffers...\n\t@mkdir -p $(LOCAL_DIR)\n\t@curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/$(PROTOC_ZIP)\n\t@mkdir -p $(dir $@)\n\t@unzip -jo $(PROTOC_ZIP) bin/protoc -d $(dir $@)\n\t@unzip -o $(PROTOC_ZIP) 'include/*' -d $(dir $@)\n\t@rm -f $(PROTOC_ZIP)\n\n.PHONY: protoc-gen-swift\nprotoc-gen-swift:\n\t@$(SWIFT) build --product protoc-gen-swift\n\t@$(SWIFT) build --product protoc-gen-grpc-swift\n\n.PHONY: protos\nprotos: $(PROTOC) protoc-gen-swift\n\t@echo Generating protocol buffers source code...\n\t@$(PROTOC) Sources/Containerization/SandboxContext/SandboxContext.proto \\\n\t\t--plugin=protoc-gen-grpc-swift=$(BUILD_BIN_DIR)/protoc-gen-grpc-swift \\\n\t\t--plugin=protoc-gen-swift=$(BUILD_BIN_DIR)/protoc-gen-swift \\\n\t\t--proto_path=Sources/Containerization/SandboxContext \\\n\t\t--grpc-swift_out=\"Sources/Containerization/SandboxContext\" \\\n\t\t--grpc-swift_opt=Visibility=Public \\\n\t\t--swift_out=\"Sources/Containerization/SandboxContext\" \\\n\t\t--swift_opt=Visibility=Public \\\n\t\t-I.\n\t@\"$(MAKE)\" update-licenses\n\n.PHONY: clean-proto-tools\nclean-proto-tools:\n\t@echo Cleaning proto tools...\n\t@rm -rf $(LOCAL_DIR)/bin/protoc*\n"
  },
  {
    "path": "README.md",
    "content": "# Containerization\n\nThe Containerization package allows applications to use Linux containers.\nContainerization is written in [Swift](https://www.swift.org) and uses [Virtualization.framework](https://developer.apple.com/documentation/virtualization) on Apple silicon.\n\n> **Looking for command line binaries for running containers?**\\\n> They are available in the dedicated [apple/container](https://github.com/apple/container) repository.\n\nContainerization provides APIs to:\n\n- [Manage OCI images](./Sources/ContainerizationOCI/).\n- [Interact with remote registries](./Sources/ContainerizationOCI/Client/).\n- [Create and populate ext4 file systems](./Sources/ContainerizationEXT4/).\n- [Interact with the Netlink socket family](./Sources/ContainerizationNetlink/).\n- [Create an optimized Linux kernel for fast boot times](./kernel/).\n- [Spawn lightweight virtual machines and manage the runtime environment](./Sources/Containerization/LinuxContainer.swift).\n- [Spawn and interact with containerized processes](./Sources/Containerization/LinuxProcess.swift).\n- Use Rosetta 2 for running linux/amd64 containers on Apple silicon.\n\nPlease view the [API documentation](https://apple.github.io/containerization/documentation/) for information on the Swift packages that Containerization provides.\n\n## Design\n\nContainerization executes each Linux container inside of its own lightweight virtual machine. Clients can create dedicated IP addresses for every container to remove the need for individual port forwarding. Containers achieve sub-second start times using an optimized [Linux kernel configuration](/kernel) and a minimal root filesystem with a lightweight init system.\n\n[vminitd](/vminitd) is a small init system, which is a subproject within Containerization.\n`vminitd` is spawned as the initial process inside of the virtual machine and provides a GRPC API over vsock.\nThe API allows the runtime environment to be configured and containerized processes to be launched.\n`vminitd` provides I/O, signals, and events to the calling process when a process is run.\n\n## Requirements\n\nTo build the Containerization package, you need:\n\n- Mac with Apple silicon\n- macOS 26\n- Xcode 26\n\nOlder versions of macOS are not supported. \n\n## Example Usage\n\nFor examples of how to use some of the libraries surface, the cctl executable is a good start. This app is a useful playground for exploring the API. It contains commands that exercise some of the core functionality of the various products, such as:\n\n1. [Manipulating OCI images](./Sources/cctl/ImageCommand.swift)\n2. [Logging in to container registries](./Sources/cctl/LoginCommand.swift)\n3. [Creating root filesystem blocks](./Sources/cctl/RootfsCommand.swift)\n4. [Running simple Linux containers](./Sources/cctl/RunCommand.swift)\n\n## Linux kernel\n\nA Linux kernel is required for spawning lightweight virtual machines on macOS.\nContainerization provides an optimized kernel configuration located in the [kernel](./kernel) directory.\n\nThis directory includes a containerized build environment to easily compile a kernel for use with Containerization.\n\nThe kernel configuration is a minimal set of features to support fast start times and a light weight environment.\n\nWhile this configuration will work for the majority of workloads we understand that some will need extra features.\nTo solve this Containerization provides first class APIs to use different kernel configurations and versions on a per container basis.\nThis enables containers to be developed and validated across different kernel versions.\n\nSee the [README](/kernel/README.md) in the kernel directory for instructions on how to compile the optimized kernel.\n\n### Kernel Support\n\nContainerization allows user provided kernels but tests functionality starting with kernel version `6.14.9`.\n\n### Pre-built Kernel\n\nIf you wish to consume a pre-built kernel, make sure it has `VIRTIO` drivers compiled into the kernel (not merely as modules).\n\nThe [Kata Containers](https://github.com/kata-containers/kata-containers) project provides a Linux kernel that is optimized for containers, with all required configuration options enabled. The [releases](https://github.com/kata-containers/kata-containers/releases/) page contains downloadable artifacts, and the image itself (`vmlinux.container`) can be found in the `/opt/kata/share/kata-containers/` directory. \n\n## Prepare to build package\n\nInstall the recommended version of Xcode.\n\nSet the active developer directory to the installed Xcode (replace `<PATH_TO_XCODE>`):\n```bash\nsudo xcode-select -s <PATH_TO_XCODE>\n``` \n\nInstall [Swiftly](https://github.com/swiftlang/swiftly), [Swift](https://www.swift.org), and [Static Linux SDK](https://www.swift.org/documentation/articles/static-linux-getting-started.html):\n\n```bash\nmake cross-prep\n```\n\nIf you use a custom terminal application, you may need to move this command from `.zprofile` to `.zshrc` (replace `<USERNAME>`):\n\n```bash\n# Added by swiftly\n. \"/Users/<USERNAME>/.swiftly/env.sh\"\n```\n\nRestart the terminal application. Ensure this command returns `/Users/<USERNAME>/.swiftly/bin/swift` (replace `<USERNAME>`):\n\n```bash\nwhich swift\n```\n\nIf you've installed or used a Static Linux SDK previously, you may need to remove older SDK versions from the system (replace `<SDK-ID>`):\n\n```bash\nswift sdk list\nswift sdk remove <SDK-ID>\n```\n\n## Build the package\n\nBuild Containerization from sources:\n\n```bash\nmake all\n```\n\n## Test the package\n\nAfter building, run basic and integration tests:\n\n```bash\nmake test integration\n```\n\nA kernel is required to run integration tests.\nIf you do not have a kernel locally for use a default kernel can be fetched using the `make fetch-default-kernel` target.\n\nFetching the default kernel only needs to happen after an initial build or after a `make clean`.\n\n```bash\nmake fetch-default-kernel\nmake all test integration\n```\n\n## Protobufs\n\nContainerization depends on specific versions of `grpc-swift` and `swift-protobuf`. You can install them and re-generate RPC interfaces with:\n\n```bash\nmake protos\n```\n\n## Building a kernel\n\nIf you'd like to build your own kernel please see the instructions in the [kernel directory](./kernel/README.md).\n\n## Pre-commit hook\n\nRun `make pre-commit` to install a pre-commit hook that ensures that your changes have correct formatting and license headers when you run `git commit`.\n\n## Documentation\n\nGenerate the API documentation for local viewing with:\n\n```bash\nmake docs\nmake serve-docs\n```\n\nPreview the documentation by running in another terminal:\n\n```bash\nopen http://localhost:8000/containerization/documentation/\n```\n\n## Contributing\n\nContributions to Containerization are welcomed and encouraged. Please see [CONTRIBUTING.md](/CONTRIBUTING.md) for more information.\n\n## Project Status\n\nVersion 0.1.0 is the first official release of Containerization. Earlier versions have no source stability guarantees.\n\nBecause the Containerization library is under active development, source stability is only guaranteed within minor versions (for example, between 0.1.1 and 0.1.2). If you don't want potentially source-breaking package updates, you can specify your package dependency using .upToNextMinorVersion(from: \"0.1.0\") instead.\n\nFuture minor versions of the package may introduce changes to these rules as needed.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security disclosure process\n\nIf you believe that you have discovered a security or privacy vulnerability in our open source software, please report it to us using the [GitHub private vulnerability feature](https://github.com/apple/containerization/security/advisories/new). Reports should include specific product and software version(s) that you believe are affected; a technical description of the behavior that you observed and the behavior that you expected; the steps required to reproduce the issue; and a proof of concept or exploit.\n\nThe project team will do their best to acknowledge receiving all security reports within 7 days of submission. This initial acknowledgment is neither acceptance nor rejection of your report. The project team may come back to you with further questions or invite you to collaborate while working through the details of your report.\n\nKeep these additional guidelines in mind when submitting your report:\n\n* Reports concerning known, publicly disclosed CVEs can be submitted as normal issues to this project.\n* Output from automated security scans or fuzzers MUST include additional context demonstrating the vulnerability with a proof of concept or working exploit.\n* Application crashes due to malformed inputs are typically not treated as security vulnerabilities, unless they are shown to also impact other processes on the system.\n\nWhile we welcome reports for open source software projects, they are not eligible for Apple Security Bounties.\n"
  },
  {
    "path": "Sources/CShim/capability.c",
    "content": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#if defined(__linux__)\n\n#include <sys/syscall.h>\n#include <unistd.h>\n#include \"capability.h\"\n\n// Capability syscall wrappers\nint CZ_capget(void *header, void *data) {\n    return syscall(SYS_capget, header, data);\n}\n\nint CZ_capset(void *header, void *data) {\n    return syscall(SYS_capset, header, data);\n}\n\n#endif\n"
  },
  {
    "path": "Sources/CShim/exec_command.c",
    "content": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#if defined(__linux__) || defined(__APPLE__)\n\n#include <errno.h>\n#include <fcntl.h>\n#include <dirent.h>\n#include <limits.h>\n#include <pthread.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <sys/resource.h>\n#include <sys/syscall.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <unistd.h>\n#if defined(__linux__)\n#include <sys/prctl.h>\n#endif\n\n#include \"exec_command.h\"\n\n#ifndef SYS_close_range\n#define SYS_close_range 436\n#endif\n\n#ifndef CLOSE_RANGE_CLOEXEC\n#define CLOSE_RANGE_CLOEXEC 0x4\n#endif\n\nstatic int mark_cloexec(int fd) {\n    int flags = fcntl(fd, F_GETFD);\n\n    if (flags == -1) return flags;\n    if (flags & FD_CLOEXEC) return 0;\n\n    return fcntl(fd, F_SETFD, flags | FD_CLOEXEC);\n}\n\nstatic int cloexec_from(int min_fd) {\n#if defined(__linux__)\n    // First try close_range.\n    long ret = syscall(SYS_close_range, min_fd, ~0U, CLOSE_RANGE_CLOEXEC);\n    if (ret == 0) {\n      return 0;\n    }\n    const char* dirpath = \"/proc/self/fd\";\n#elif defined(__APPLE__)\n    const char* dirpath = \"/dev/fd\";\n#endif\n    DIR *dp = opendir(dirpath);\n    if (!dp) return -1;\n\n    int dp_fd = dirfd(dp);\n    struct dirent *de;\n\n    while ((de = readdir(dp))) {\n      if (de->d_name[0] == '.') continue;\n\n      char *end;\n      long val = strtol(de->d_name, &end, 10);\n      if (*end || val < 0 || val > INT_MAX) continue;\n\n      int fd = (int)val;\n      if (fd < min_fd || fd == dp_fd) continue;\n\n      int ret = mark_cloexec(fd);\n      if (ret != 0) {\n        return ret;\n      }\n    }\n    close(dp_fd);\n    closedir(dp);\n    return 0;\n}\n\nvoid exec_command_attrs_init(struct exec_command_attrs *attrs) {\n  attrs->setpgid = 0;\n  attrs->pgid = 0;\n  attrs->setsid = 0;\n  attrs->setctty = 0;\n  attrs->ctty = 0;\n  attrs->mask = 0;\n  attrs->uid = -1;\n  attrs->gid = -1;\n  attrs->pdeathSignal = 0;\n  attrs->setfgpgrp = 0;\n}\n\nstatic void child_handler(const int sync_pipes[2], const char *executable,\n                          char *const args[], char *const environment[],\n                          const int file_handles[], const int file_handle_count,\n                          const char *cwd, const sigset_t old_mask,\n                          const struct exec_command_attrs attrs) {\n  int i = 0;\n  int err = 0;\n  int fd_index = 0;\n  int fd_table[file_handle_count];\n  struct rlimit limits = {0};\n  int syncfd = sync_pipes[1];\n  struct sigaction action = {0};\n\n  // Closing our parent's side of the pipe\n  if (close(sync_pipes[0]) < 0) {\n    goto fail;\n  }\n\n  // Setup process group and foreground before clearing signal mask.\n  if (attrs.setpgid) {\n    if (setpgid(0, attrs.pgid) < 0) {\n      goto fail;\n    }\n  }\n\n  // Make the new process group the foreground process group so it can read from the TTY.\n  if (attrs.setfgpgrp) {\n    if (tcsetpgrp(STDIN_FILENO, getpgrp()) < 0) {\n      if (errno != ENOTTY && errno != ENXIO) {\n        goto fail;\n      }\n    }\n  }\n\n  // clear sighandlers\n  action.sa_flags = 0;\n  action.sa_handler = SIG_DFL;\n  sigemptyset(&action.sa_mask);\n  for (i = 0; i < NSIG; i++) {\n    sigaction(i, &action, 0);\n  }\n\n  sigset_t local_mask;\n  sigemptyset(&local_mask);\n  if (pthread_sigmask(SIG_SETMASK, &local_mask, NULL) < 0) {\n    goto fail;\n  }\n\n  // start shuffling fds.\n  // look at all the file handles and find the highest one,\n  // use that for our pipe,\n  //\n  // Then, we need to start dup2 the fds starting for the final process\n  // at 0-n.\n  // as an example we have this list of FDs that should be passed to the\n  // process:\n  //\n  /*\n   The index of this list is the final result that the new process expects.\n   The values are open fds provided from the parent process.\n   [0] == 12\n   [1] == 7\n   [2] == 9\n   [3] == 0\n\n   We also have a pipe to sync the child and parent so that adds an additional\n   parameter to consider.\n\n   So we start by finding the highest open fd in the list, then move our pipe to\n   the next.\n\n   i.e. fd12 is highest so move our pipe to fd13\n\n   Now start moving all the fds above our pipe as we will need to start placing\n   the fds in the child process into the right order. Make sure they are all\n   marked cloexec.\n\n   pipe == 13\n   [0] == 12 dup2 14\n   [1] == 7 dup2 15\n   [2] == 9 dup2 16\n   [3] == 0 dup2 17\n\n   Now overwrite the fd table for the child with the current index.\n\n   Make index == fd.\n\n   pipe == 13\n   [0] == 14 dup2 0\n   [1] == 15 dup2 1\n   [2] == 16 dup2 2\n   [3] == 17 dup2 3\n\n   Clear cloexec on this new fds.\n   */\n\n  // find the highest fd value in our list.\n  for (i = 0; i < file_handle_count; i++) {\n    if (file_handles[i] > fd_index) {\n      fd_index = file_handles[i];\n    }\n    fd_table[i] = file_handles[i];\n  }\n  // now fd_index is == to the highest fd in our list of handles.\n  // Increment it and set our pipe to it.\n  fd_index++;\n\n  if (syncfd != fd_index) {\n    if (dup2(syncfd, fd_index) < 0) {\n      goto fail;\n    }\n    if (close(syncfd) < 0) {\n      goto fail;\n    }\n    syncfd = fd_index;\n  }\n  fd_index++;\n\n  // make sure our syncfd retains its cloexec\n  if (fcntl(syncfd, F_SETFD, FD_CLOEXEC) == -1) {\n    goto fail;\n  }\n\n  // move the rest of the fds up above our index if they don't match the index.\n  for (i = 0; i < file_handle_count; i++) {\n    if (fd_table[i] == i) {\n      continue;\n    }\n    if (dup2(fd_table[i], fd_index) < 0) {\n      goto fail;\n    }\n    if (fcntl(fd_index, F_SETFD, FD_CLOEXEC) == -1) {\n      goto fail;\n    }\n    fd_table[i] = fd_index;\n    fd_index++;\n  }\n\n  // now create the child process's final fd table. where i == i\n  for (i = 0; i < file_handle_count; i++) {\n    if (fd_table[i] != i) {\n      if (dup2(fd_table[i], i) < 0) {\n        goto fail;\n      }\n    }\n    // now fd[i] should == i\n    // clear cloexec as this fd is where we want it.\n    if (fcntl(i, F_SETFD, 0) == -1) {\n      goto fail;\n    }\n  }\n\n  if (attrs.setsid) {\n    if (setsid() == -1) {\n      goto fail;\n    }\n  }\n\n  if (attrs.setctty) {\n    if (ioctl(attrs.ctty, TIOCSCTTY, 0)) {\n      goto fail;\n    }\n  }\n\n#if defined(__linux__)\n  // Set parent death signal if specified\n  if (attrs.pdeathSignal != 0) {\n    if (prctl(PR_SET_PDEATHSIG, attrs.pdeathSignal) != 0) {\n      goto fail;\n    }\n  }\n#endif\n\n  // close exec everything outside of our child's fd_table.\n  if (cloexec_from(file_handle_count) != 0) {\n    goto fail;\n  }\n\n  // set gid\n  if (attrs.gid != -1) {\n    if (setgid(attrs.gid) != 0) {\n      goto fail;\n    }\n  }\n\n  // set uid\n  if (attrs.uid != -1) {\n    if (setreuid(attrs.uid, attrs.uid) != 0) {\n      goto fail;\n    }\n  }\n\n  if (cwd != NULL) {\n    if (chdir(cwd)) {\n      goto fail;\n    }\n  }\n\n  execve(executable, args, environment);\nfail:\n  err = errno;\n  if (err) {\n    // send our error to the parent\n    while (write(syncfd, &err, sizeof(err)) < 0)\n      ;\n  }\n  exit(127);\n}\n\nint exec_command(pid_t *result, const char *executable, char *const args[],\n                 char *const envp[], const int file_handles[],\n                 const int file_handle_count, const char *working_directory,\n                 struct exec_command_attrs *attrs) {\n  pid_t pid = 0;\n  int err = 0;\n  int sync_pipe[2];\n  sigset_t old_mask;\n\n  sigset_t all;\n  sigfillset(&all);\n\n  if (pipe(sync_pipe)) {\n    goto fail;\n  }\n\n  if (pthread_sigmask(SIG_SETMASK, &all, &old_mask) < 0) {\n    goto fail;\n  }\n\n  pid = fork();\n  if (pid == -1) {\n    close(sync_pipe[0]);\n    close(sync_pipe[1]);\n    goto fail;\n  }\n\n  if (pid == 0) {\n    // hand off to child\n    child_handler(sync_pipe, executable, args, envp, file_handles,\n                  file_handle_count, working_directory, old_mask, *attrs);\n    exit(EXIT_FAILURE);\n  }\n\n  // handle parent operations\n  if (close(sync_pipe[1]) < 0) {\n    goto fail;\n  }\n\n  // sync with our child process\n  err = 0;\n  ssize_t size = read(sync_pipe[0], &err, sizeof(err));\n  // -- we didn't get an errno back\n  if (size != sizeof(err)) {\n    // will be used as return result\n    err = 0;\n  } else {\n    // we did get an errno back from the child process and our\n    // err var is set to that errno\n    // lets set our errno and then reap the process\n    errno = err;\n    int status = 0;\n    waitpid(pid, &status, 0);\n    // lets continue our journey below\n  }\n\n  if (close(sync_pipe[0]) < 0) {\n    goto fail;\n  }\n  if (err) {\n    goto fail;\n  }\n\n  (*result) = pid;\n  err = 0;\nfail:\n  if (pthread_sigmask(SIG_SETMASK, &old_mask, 0) < 0) {\n    printf(\"restoring signal mask: %s\\n\", strerror(errno));\n  }\n  if (err) {\n    printf(\"exec_command execve: %s\\n\", strerror(err));\n    return -1;\n  }\n  return 0;\n}\n\n#endif"
  },
  {
    "path": "Sources/CShim/include/capability.h",
    "content": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CAPABILITY_H\n#define __CAPABILITY_H\n\n#if defined(__linux__)\n\n// Capability syscall wrappers\nint CZ_capget(void *header, void *data);\nint CZ_capset(void *header, void *data);\n\n#endif\n\n#endif\n"
  },
  {
    "path": "Sources/CShim/include/exec_command.h",
    "content": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef exec_command_h\n#define exec_command_h\n\n#if defined(__linux__) || defined(__APPLE__)\n\n#include <sys/types.h>\n#include <unistd.h>\n\nstruct exec_command_attrs {\n  int setpgid;\n  /// parent group id\n  pid_t pgid;\n  /// set the controlling terminal\n  int setctty;\n  /// controlling terminal fd\n  int ctty;\n  /// set the process as session leader\n  int setsid;\n  /// set the process user id\n  uid_t uid;\n  /// set the process group id\n  gid_t gid;\n  /// signal mask for the child process\n  int mask;\n  /// parent death signal (Linux only, 0 to disable)\n  int pdeathSignal;\n  /// make the new process group the foreground process group\n  int setfgpgrp;\n};\n\nvoid exec_command_attrs_init(struct exec_command_attrs *attrs);\n\n/// spawn a new child process with the provided attrs\nint exec_command(pid_t *result, const char *executable, char *const argv[],\n                 char *const envp[], const int file_handles[],\n                 const int file_handle_count, const char *working_directory,\n                 struct exec_command_attrs *attrs);\n\n#endif /* defined(__linux__) || defined(__APPLE__) */\n#endif /* exec_command_h */\n"
  },
  {
    "path": "Sources/CShim/include/openat2.h",
    "content": "/*\n * Copyright © 2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __OPENAT2_H\n#define __OPENAT2_H\n\n#include <sys/types.h>\n\n#ifndef RESOLVE_IN_ROOT\n#define RESOLVE_IN_ROOT 0x10\n#endif\n\nstruct cz_open_how {\n  unsigned long long flags;\n  unsigned long long mode;\n  unsigned long long resolve;\n};\n\n/// openat2(2) wrapper. Musl does not provide openat2 so we invoke the syscall\n/// directly. Requires Linux 5.6+.\nint CZ_openat2(int dirfd, const char *pathname, struct cz_open_how *how,\n               size_t size);\n\n#endif\n"
  },
  {
    "path": "Sources/CShim/include/prctl.h",
    "content": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __PRCTL_H\n#define __PRCTL_H\n\n#if defined(__linux__)\n\n#include <sys/types.h>\n\n// Capability management prctl wrappers\nint CZ_prctl_set_keepcaps();\nint CZ_prctl_clear_keepcaps();\nint CZ_prctl_capbset_drop(unsigned int capability);\nint CZ_prctl_cap_ambient_clear_all();\nint CZ_prctl_cap_ambient_raise(unsigned int capability);\n\n#endif\n\n#endif\n"
  },
  {
    "path": "Sources/CShim/include/socket_helpers.h",
    "content": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef socket_helpers_h\n#define socket_helpers_h\n\n#include <sys/socket.h>\n#include <stdint.h>\n\n// Helper functions to access CMSG macros from Swift\nstruct cmsghdr* CZ_CMSG_FIRSTHDR(struct msghdr *msg);\nvoid* CZ_CMSG_DATA(struct cmsghdr *cmsg);\nsize_t CZ_CMSG_SPACE(size_t length);\nsize_t CZ_CMSG_LEN(size_t length);\n\n#endif /* socket_helpers_h */\n"
  },
  {
    "path": "Sources/CShim/include/vsock.h",
    "content": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n\n#ifndef vsock_h\n#define vsock_h\n\n#include <sys/ioctl.h>\n\n#ifdef __APPLE__\n    #include <sys/vsock.h>\n#else\n    #include <sys/socket.h>\n    #include <linux/vm_sockets.h>\n#endif /* __APPLE__ */\n\nextern const unsigned long VsockLocalCIDIoctl;\n\n#endif /* vsock_h */\n"
  },
  {
    "path": "Sources/CShim/openat2.c",
    "content": "/*\n * Copyright © 2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#if defined(__linux__)\n\n#include <sys/syscall.h>\n#include <unistd.h>\n\n#include \"openat2.h\"\n\n#ifndef SYS_openat2\n#define SYS_openat2 437\n#endif\n\nint CZ_openat2(int dirfd, const char *pathname, struct cz_open_how *how,\n               size_t size) {\n  return syscall(SYS_openat2, dirfd, pathname, how, size);\n}\n\n#endif\n"
  },
  {
    "path": "Sources/CShim/prctl.c",
    "content": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#if defined(__linux__)\n\n#include <sys/prctl.h>\n#include \"prctl.h\"\n\n// Set keep caps to preserve capabilities across setuid()\nint CZ_prctl_set_keepcaps() {\n    return prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);\n}\n\n// Clear keep caps after user change\nint CZ_prctl_clear_keepcaps() {\n    return prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0);\n}\n\n// Drop capability from bounding set\nint CZ_prctl_capbset_drop(unsigned int capability) {\n    return prctl(PR_CAPBSET_DROP, capability, 0, 0, 0);\n}\n\n// Clear all ambient capabilities\nint CZ_prctl_cap_ambient_clear_all() {\n    return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);\n}\n\n// Raise ambient capability\nint CZ_prctl_cap_ambient_raise(unsigned int capability) {\n    return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, capability, 0, 0);\n}\n\n#endif\n"
  },
  {
    "path": "Sources/CShim/socket_helpers.c",
    "content": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"socket_helpers.h\"\n\nstruct cmsghdr* CZ_CMSG_FIRSTHDR(struct msghdr *msg) {\n    return CMSG_FIRSTHDR(msg);\n}\n\nvoid* CZ_CMSG_DATA(struct cmsghdr *cmsg) {\n    return CMSG_DATA(cmsg);\n}\n\nsize_t CZ_CMSG_SPACE(size_t length) {\n    return CMSG_SPACE(length);\n}\n\nsize_t CZ_CMSG_LEN(size_t length) {\n    return CMSG_LEN(length);\n}\n"
  },
  {
    "path": "Sources/CShim/vsock.c",
    "content": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"vsock.h\"\n\nconst unsigned long VsockLocalCIDIoctl = IOCTL_VM_SOCKETS_GET_LOCAL_CID;\n"
  },
  {
    "path": "Sources/Containerization/AttachedFilesystem.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationExtras\nimport ContainerizationOCI\n\n/// A filesystem that was attached and able to be mounted inside the runtime environment.\npublic struct AttachedFilesystem: Sendable {\n    /// The type of the filesystem.\n    public var type: String\n    /// The path to the filesystem within a sandbox.\n    public var source: String\n    /// Destination when mounting the filesystem inside a sandbox.\n    public var destination: String\n    /// The options to use when mounting the filesystem.\n    public var options: [String]\n\n    #if os(macOS)\n    public init(mount: Mount, allocator: any AddressAllocator<Character>) throws {\n        switch mount.type {\n        case \"virtiofs\":\n            let name = try hashMountSource(source: mount.source)\n            self.source = name\n        case \"ext4\":\n            let char = try allocator.allocate()\n            self.source = \"/dev/vd\\(char)\"\n        default:\n            self.source = mount.source\n        }\n        self.type = mount.type\n        self.options = mount.options\n        self.destination = mount.destination\n    }\n    #endif\n\n    public init(type: String, source: String, destination: String, options: [String]) {\n        self.type = type\n        self.source = source\n        self.destination = destination\n        self.options = options\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/Container.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// The core protocol container implementations must implement.\npublic protocol Container {\n    /// ID for the container.\n    var id: String { get }\n    /// The amount of cpus assigned to the container.\n    var cpus: Int { get }\n    /// The memory in bytes assigned to the container.\n    var memoryInBytes: UInt64 { get }\n    /// The network interfaces assigned to the container.\n    var interfaces: [any Interface] { get }\n}\n"
  },
  {
    "path": "Sources/Containerization/ContainerManager.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(macOS)\n\nimport ContainerizationError\nimport ContainerizationEXT4\nimport ContainerizationOCI\nimport ContainerizationOS\nimport Foundation\nimport ContainerizationExtras\nimport SystemPackage\nimport Virtualization\n\n/// A manager for creating and running containers.\n/// Supports container networking options.\npublic struct ContainerManager: Sendable {\n    public let imageStore: ImageStore\n    private let vmm: VirtualMachineManager\n    private var network: Network?\n\n    private var containerRoot: URL {\n        self.imageStore.path.appendingPathComponent(\"containers\")\n    }\n\n    /// Create a new manager with the provided kernel, initfs mount, image store\n    /// and optional network implementation. This will use a Virtualization.framework\n    /// backed VMM implicitly.\n    public init(\n        kernel: Kernel,\n        initfs: Mount,\n        imageStore: ImageStore,\n        network: Network? = nil,\n        rosetta: Bool = false,\n        nestedVirtualization: Bool = false\n    ) throws {\n        self.imageStore = imageStore\n        self.network = network\n        try Self.createRootDirectory(path: self.imageStore.path)\n        self.vmm = VZVirtualMachineManager(\n            kernel: kernel,\n            initialFilesystem: initfs,\n            rosetta: rosetta,\n            nestedVirtualization: nestedVirtualization\n        )\n    }\n\n    /// Create a new manager with the provided kernel, initfs mount, root state\n    /// directory and optional network implementation. This will use a Virtualization.framework\n    /// backed VMM implicitly.\n    public init(\n        kernel: Kernel,\n        initfs: Mount,\n        root: URL? = nil,\n        network: Network? = nil,\n        rosetta: Bool = false,\n        nestedVirtualization: Bool = false\n    ) throws {\n        if let root {\n            self.imageStore = try ImageStore(path: root)\n        } else {\n            self.imageStore = ImageStore.default\n        }\n        self.network = network\n        try Self.createRootDirectory(path: self.imageStore.path)\n        self.vmm = VZVirtualMachineManager(\n            kernel: kernel,\n            initialFilesystem: initfs,\n            rosetta: rosetta,\n            nestedVirtualization: nestedVirtualization\n        )\n    }\n\n    /// Create a new manager with the provided kernel, initfs reference, image store\n    /// and optional network implementation. This will use a Virtualization.framework\n    /// backed VMM implicitly.\n    public init(\n        kernel: Kernel,\n        initfsReference: String,\n        imageStore: ImageStore,\n        network: Network? = nil,\n        rosetta: Bool = false,\n        nestedVirtualization: Bool = false\n    ) async throws {\n        self.imageStore = imageStore\n        self.network = network\n        try Self.createRootDirectory(path: self.imageStore.path)\n\n        let initPath = self.imageStore.path.appendingPathComponent(\"initfs.ext4\")\n        let initImage = try await self.imageStore.getInitImage(reference: initfsReference)\n        let initfs = try await {\n            do {\n                return try await initImage.initBlock(at: initPath, for: .linuxArm)\n            } catch let err as ContainerizationError {\n                guard err.code == .exists else {\n                    throw err\n                }\n                return .block(\n                    format: \"ext4\",\n                    source: initPath.absolutePath(),\n                    destination: \"/\",\n                    options: [\"ro\"]\n                )\n            }\n        }()\n\n        self.vmm = VZVirtualMachineManager(\n            kernel: kernel,\n            initialFilesystem: initfs,\n            rosetta: rosetta,\n            nestedVirtualization: nestedVirtualization\n        )\n    }\n\n    /// Create a new manager with the provided kernel and image reference for the initfs.\n    /// This will use a Virtualization.framework backed VMM implicitly.\n    public init(\n        kernel: Kernel,\n        initfsReference: String,\n        root: URL? = nil,\n        network: Network? = nil,\n        rosetta: Bool = false,\n        nestedVirtualization: Bool = false\n    ) async throws {\n        if let root {\n            self.imageStore = try ImageStore(path: root)\n        } else {\n            self.imageStore = ImageStore.default\n        }\n        self.network = network\n        try Self.createRootDirectory(path: self.imageStore.path)\n\n        let initPath = self.imageStore.path.appendingPathComponent(\"initfs.ext4\")\n        let initImage = try await self.imageStore.getInitImage(reference: initfsReference)\n        let initfs = try await {\n            do {\n                return try await initImage.initBlock(at: initPath, for: .linuxArm)\n            } catch let err as ContainerizationError {\n                guard err.code == .exists else {\n                    throw err\n                }\n                return .block(\n                    format: \"ext4\",\n                    source: initPath.absolutePath(),\n                    destination: \"/\",\n                    options: [\"ro\"]\n                )\n            }\n        }()\n\n        self.vmm = VZVirtualMachineManager(\n            kernel: kernel,\n            initialFilesystem: initfs,\n            rosetta: rosetta,\n            nestedVirtualization: nestedVirtualization\n        )\n    }\n\n    /// Create a new manager with the provided vmm and network.\n    public init(\n        vmm: any VirtualMachineManager,\n        network: Network? = nil\n    ) throws {\n        self.imageStore = ImageStore.default\n        try Self.createRootDirectory(path: self.imageStore.path)\n        self.network = network\n        self.vmm = vmm\n    }\n\n    private static func createRootDirectory(path: URL) throws {\n        try FileManager.default.createDirectory(\n            at: path.appendingPathComponent(\"containers\"),\n            withIntermediateDirectories: true\n        )\n    }\n\n    /// Returns a new container from the provided image reference.\n    /// - Parameters:\n    ///   - id: The container ID.\n    ///   - reference: The image reference.\n    ///   - rootfsSizeInBytes: The size of the root filesystem in bytes. Defaults to 8 GiB.\n    ///   - writableLayerSizeInBytes: Optional size for a separate writable layer. When provided,\n    ///     the rootfs becomes read-only and an overlayfs is used with a separate writable layer of this size.\n    ///   - readOnly: Whether to mount the root filesystem as read-only.\n    ///   - networking: Whether to create a network interface for this container. Defaults to `true`.\n    ///     When `false`, no network resources are allocated and `releaseNetwork`/`delete` remain safe to call.\n    public mutating func create(\n        _ id: String,\n        reference: String,\n        rootfsSizeInBytes: UInt64 = 8.gib(),\n        writableLayerSizeInBytes: UInt64? = nil,\n        readOnly: Bool = false,\n        networking: Bool = true,\n        configuration: (inout LinuxContainer.Configuration) throws -> Void\n    ) async throws -> LinuxContainer {\n        let image = try await imageStore.get(reference: reference, pull: true)\n        return try await create(\n            id,\n            image: image,\n            rootfsSizeInBytes: rootfsSizeInBytes,\n            writableLayerSizeInBytes: writableLayerSizeInBytes,\n            readOnly: readOnly,\n            networking: networking,\n            configuration: configuration\n        )\n    }\n\n    /// Returns a new container from the provided image.\n    /// - Parameters:\n    ///   - id: The container ID.\n    ///   - image: The image.\n    ///   - rootfsSizeInBytes: The size of the root filesystem in bytes. Defaults to 8 GiB.\n    ///   - writableLayerSizeInBytes: Optional size for a separate writable layer. When provided,\n    ///     the rootfs becomes read-only and an overlayfs is used with a separate writable layer of this size.\n    ///   - readOnly: Whether to mount the root filesystem as read-only.\n    ///   - networking: Whether to create a network interface for this container. Defaults to `true`.\n    ///     When `false`, no network resources are allocated and `releaseNetwork`/`delete` remain safe to call.\n    public mutating func create(\n        _ id: String,\n        image: Image,\n        rootfsSizeInBytes: UInt64 = 8.gib(),\n        writableLayerSizeInBytes: UInt64? = nil,\n        readOnly: Bool = false,\n        networking: Bool = true,\n        configuration: (inout LinuxContainer.Configuration) throws -> Void\n    ) async throws -> LinuxContainer {\n        let path = try createContainerRoot(id)\n\n        var rootfs = try await unpack(\n            image: image,\n            destination: path.appendingPathComponent(\"rootfs.ext4\"),\n            size: rootfsSizeInBytes\n        )\n        if readOnly {\n            rootfs.options.append(\"ro\")\n        }\n\n        // Create writable layer if size is specified.\n        var writableLayer: Mount? = nil\n        if let writableLayerSize = writableLayerSizeInBytes {\n            writableLayer = try createEmptyFilesystem(\n                at: path.appendingPathComponent(\"writable.ext4\"),\n                size: writableLayerSize\n            )\n        }\n\n        return try await create(\n            id,\n            image: image,\n            rootfs: rootfs,\n            writableLayer: writableLayer,\n            networking: networking,\n            configuration: configuration\n        )\n    }\n\n    /// Returns a new container from the provided image and root filesystem mount.\n    /// - Parameters:\n    ///   - id: The container ID.\n    ///   - image: The image.\n    ///   - rootfs: The root filesystem mount pointing to an existing block file.\n    ///     The `destination` field is ignored as mounting is handled internally.\n    ///   - writableLayer: Optional writable layer mount. When provided, an overlayfs is used with\n    ///     rootfs as the lower layer and this as the upper layer.\n    ///     The `destination` field is ignored as mounting is handled internally.\n    ///   - networking: Whether to create a network interface for this container. Defaults to `true`.\n    ///     When `false`, no network resources are allocated and `releaseNetwork`/`delete` remain safe to call.\n    public mutating func create(\n        _ id: String,\n        image: Image,\n        rootfs: Mount,\n        writableLayer: Mount? = nil,\n        networking: Bool = true,\n        configuration: (inout LinuxContainer.Configuration) throws -> Void\n    ) async throws -> LinuxContainer {\n        let imageConfig = try await image.config(for: .current).config\n        return try LinuxContainer(\n            id,\n            rootfs: rootfs,\n            writableLayer: writableLayer,\n            vmm: self.vmm\n        ) { config in\n            if let imageConfig {\n                config.process = .init(from: imageConfig)\n            }\n            if networking, let interface = try self.network?.createInterface(id) {\n                config.interfaces = [interface]\n                guard let gateway = interface.ipv4Gateway else {\n                    throw ContainerizationError(\n                        .invalidState,\n                        message: \"missing ipv4 gateway for container \\(id)\"\n                    )\n                }\n                config.dns = .init(nameservers: [gateway.description])\n            }\n            config.bootLog = BootLog.file(path: self.containerRoot.appendingPathComponent(id).appendingPathComponent(\"bootlog.log\"))\n            try configuration(&config)\n        }\n    }\n\n    /// Releases network resources for a container.\n    ///\n    /// - Parameter id: The container ID.\n    public mutating func releaseNetwork(_ id: String) throws {\n        try self.network?.releaseInterface(id)\n    }\n\n    /// Releases network resources and removes all files for a container.\n    /// - Parameter id: The container ID.\n    public mutating func delete(_ id: String) throws {\n        try self.releaseNetwork(id)\n        let path = containerRoot.appendingPathComponent(id)\n        try FileManager.default.removeItem(at: path)\n    }\n\n    private func createContainerRoot(_ id: String) throws -> URL {\n        let path = containerRoot.appendingPathComponent(id)\n        try FileManager.default.createDirectory(at: path, withIntermediateDirectories: false)\n        return path\n    }\n\n    private func unpack(image: Image, destination: URL, size: UInt64) async throws -> Mount {\n        do {\n            let unpacker = EXT4Unpacker(blockSizeInBytes: size)\n            return try await unpacker.unpack(image, for: .current, at: destination)\n        } catch let err as ContainerizationError {\n            if err.code == .exists {\n                return .block(\n                    format: \"ext4\",\n                    source: destination.absolutePath(),\n                    destination: \"/\",\n                    options: []\n                )\n            }\n            throw err\n        }\n    }\n\n    private func createEmptyFilesystem(at destination: URL, size: UInt64) throws -> Mount {\n        let path = destination.absolutePath()\n        guard !FileManager.default.fileExists(atPath: path) else {\n            throw ContainerizationError(.exists, message: \"filesystem already exists at \\(path)\")\n        }\n        let filesystem = try EXT4.Formatter(FilePath(path), minDiskSize: size)\n        try filesystem.close()\n        return .block(\n            format: \"ext4\",\n            source: path,\n            destination: \"/\",\n            options: []\n        )\n    }\n}\n\nextension CIDRv4 {\n    /// The gateway address of the network.\n    public var gateway: IPv4Address {\n        IPv4Address(self.lower.value + 1)\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Containerization/ContainerStatistics.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// Statistics for a container.\npublic struct ContainerStatistics: Sendable {\n    public var id: String\n    public var process: ProcessStatistics?\n    public var memory: MemoryStatistics?\n    public var cpu: CPUStatistics?\n    public var blockIO: BlockIOStatistics?\n    public var networks: [NetworkStatistics]?\n    public var memoryEvents: MemoryEventStatistics?\n\n    public init(\n        id: String,\n        process: ProcessStatistics? = nil,\n        memory: MemoryStatistics? = nil,\n        cpu: CPUStatistics? = nil,\n        blockIO: BlockIOStatistics? = nil,\n        networks: [NetworkStatistics]? = nil,\n        memoryEvents: MemoryEventStatistics? = nil\n    ) {\n        self.id = id\n        self.process = process\n        self.memory = memory\n        self.cpu = cpu\n        self.blockIO = blockIO\n        self.networks = networks\n        self.memoryEvents = memoryEvents\n    }\n\n    /// Process statistics for a container.\n    public struct ProcessStatistics: Sendable {\n        public var current: UInt64\n        public var limit: UInt64\n\n        public init(current: UInt64, limit: UInt64) {\n            self.current = current\n            self.limit = limit\n        }\n    }\n\n    /// Memory statistics for a container.\n    public struct MemoryStatistics: Sendable {\n        public var usageBytes: UInt64\n        public var limitBytes: UInt64\n        public var swapUsageBytes: UInt64\n        public var swapLimitBytes: UInt64\n        public var cacheBytes: UInt64\n        public var kernelStackBytes: UInt64\n        public var slabBytes: UInt64\n        public var pageFaults: UInt64\n        public var majorPageFaults: UInt64\n        public var inactiveFile: UInt64\n        public var anon: UInt64\n\n        public init(\n            usageBytes: UInt64,\n            limitBytes: UInt64,\n            swapUsageBytes: UInt64,\n            swapLimitBytes: UInt64,\n            cacheBytes: UInt64,\n            kernelStackBytes: UInt64,\n            slabBytes: UInt64,\n            pageFaults: UInt64,\n            majorPageFaults: UInt64,\n            inactiveFile: UInt64,\n            anon: UInt64\n        ) {\n            self.usageBytes = usageBytes\n            self.limitBytes = limitBytes\n            self.swapUsageBytes = swapUsageBytes\n            self.swapLimitBytes = swapLimitBytes\n            self.cacheBytes = cacheBytes\n            self.kernelStackBytes = kernelStackBytes\n            self.slabBytes = slabBytes\n            self.pageFaults = pageFaults\n            self.majorPageFaults = majorPageFaults\n            self.inactiveFile = inactiveFile\n            self.anon = anon\n        }\n    }\n\n    /// CPU statistics for a container.\n    public struct CPUStatistics: Sendable {\n        public var usageUsec: UInt64\n        public var userUsec: UInt64\n        public var systemUsec: UInt64\n        public var throttlingPeriods: UInt64\n        public var throttledPeriods: UInt64\n        public var throttledTimeUsec: UInt64\n\n        public init(\n            usageUsec: UInt64,\n            userUsec: UInt64,\n            systemUsec: UInt64,\n            throttlingPeriods: UInt64,\n            throttledPeriods: UInt64,\n            throttledTimeUsec: UInt64\n        ) {\n            self.usageUsec = usageUsec\n            self.userUsec = userUsec\n            self.systemUsec = systemUsec\n            self.throttlingPeriods = throttlingPeriods\n            self.throttledPeriods = throttledPeriods\n            self.throttledTimeUsec = throttledTimeUsec\n        }\n    }\n\n    /// Block I/O statistics for a container.\n    public struct BlockIOStatistics: Sendable {\n        public var devices: [BlockIODevice]\n\n        public init(devices: [BlockIODevice]) {\n            self.devices = devices\n        }\n    }\n\n    /// Block I/O statistics for a specific device.\n    public struct BlockIODevice: Sendable {\n        public var major: UInt64\n        public var minor: UInt64\n        public var readBytes: UInt64\n        public var writeBytes: UInt64\n        public var readOperations: UInt64\n        public var writeOperations: UInt64\n\n        public init(\n            major: UInt64,\n            minor: UInt64,\n            readBytes: UInt64,\n            writeBytes: UInt64,\n            readOperations: UInt64,\n            writeOperations: UInt64\n        ) {\n            self.major = major\n            self.minor = minor\n            self.readBytes = readBytes\n            self.writeBytes = writeBytes\n            self.readOperations = readOperations\n            self.writeOperations = writeOperations\n        }\n    }\n\n    /// Statistics for a network interface.\n    public struct NetworkStatistics: Sendable {\n        public var interface: String\n        public var receivedPackets: UInt64\n        public var transmittedPackets: UInt64\n        public var receivedBytes: UInt64\n        public var transmittedBytes: UInt64\n        public var receivedErrors: UInt64\n        public var transmittedErrors: UInt64\n\n        public init(\n            interface: String,\n            receivedPackets: UInt64,\n            transmittedPackets: UInt64,\n            receivedBytes: UInt64,\n            transmittedBytes: UInt64,\n            receivedErrors: UInt64,\n            transmittedErrors: UInt64\n        ) {\n            self.interface = interface\n            self.receivedPackets = receivedPackets\n            self.transmittedPackets = transmittedPackets\n            self.receivedBytes = receivedBytes\n            self.transmittedBytes = transmittedBytes\n            self.receivedErrors = receivedErrors\n            self.transmittedErrors = transmittedErrors\n        }\n    }\n\n    /// Memory event counters from cgroup2's memory.events file.\n    public struct MemoryEventStatistics: Sendable {\n        /// Number of times the cgroup was reclaimed due to low memory.\n        public var low: UInt64\n        /// Number of times the cgroup exceeded its high memory limit.\n        public var high: UInt64\n        /// Number of times the cgroup hit its max memory limit.\n        public var max: UInt64\n        /// Number of times the cgroup triggered OOM.\n        public var oom: UInt64\n        /// Number of processes killed by OOM killer.\n        public var oomKill: UInt64\n\n        public init(low: UInt64, high: UInt64, max: UInt64, oom: UInt64, oomKill: UInt64) {\n            self.low = low\n            self.high = high\n            self.max = max\n            self.oom = oom\n            self.oomKill = oomKill\n        }\n    }\n}\n\n/// Categories of statistics that can be requested.\npublic struct StatCategory: OptionSet, Sendable {\n    public let rawValue: Int\n\n    public init(rawValue: Int) {\n        self.rawValue = rawValue\n    }\n\n    /// Process statistics (pids.current, pids.max).\n    public static let process = StatCategory(rawValue: 1 << 0)\n    /// Memory usage statistics.\n    public static let memory = StatCategory(rawValue: 1 << 1)\n    /// CPU usage statistics.\n    public static let cpu = StatCategory(rawValue: 1 << 2)\n    /// Block I/O statistics.\n    public static let blockIO = StatCategory(rawValue: 1 << 3)\n    /// Network interface statistics.\n    public static let network = StatCategory(rawValue: 1 << 4)\n    /// Memory event counters (OOM kills, pressure events, etc.).\n    public static let memoryEvents = StatCategory(rawValue: 1 << 5)\n\n    /// All available statistics categories.\n    public static let all: StatCategory = [.process, .memory, .cpu, .blockIO, .network, .memoryEvents]\n}\n"
  },
  {
    "path": "Sources/Containerization/DNSConfiguration.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// DNS configuration for a container. The values will be used to\n/// construct /etc/resolv.conf for a given container.\npublic struct DNS: Sendable {\n    /// The set of default nameservers to use if none are provided\n    /// in the constructor.\n    public static let defaultNameservers = [\"1.1.1.1\"]\n\n    /// The nameservers a container should use.\n    public var nameservers: [String]\n    /// The DNS domain to use.\n    public var domain: String?\n    /// The DNS search domains to use.\n    public var searchDomains: [String]\n    /// The DNS options to use.\n    public var options: [String]\n\n    public init(\n        nameservers: [String] = defaultNameservers,\n        domain: String? = nil,\n        searchDomains: [String] = [],\n        options: [String] = []\n    ) {\n        self.nameservers = nameservers\n        self.domain = domain\n        self.searchDomains = searchDomains\n        self.options = options\n    }\n}\n\nextension DNS {\n    public var resolvConf: String {\n        var text = \"\"\n\n        if !nameservers.isEmpty {\n            text += nameservers.map { \"nameserver \\($0)\" }.joined(separator: \"\\n\") + \"\\n\"\n        }\n\n        if let domain {\n            text += \"domain \\(domain)\\n\"\n        }\n\n        if !searchDomains.isEmpty {\n            text += \"search \\(searchDomains.joined(separator: \" \"))\\n\"\n        }\n\n        if !options.isEmpty {\n            text += \"options \\(options.joined(separator: \" \"))\\n\"\n        }\n\n        return text\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/ExitStatus.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// ExitStatus contains the exit code for a given container process,\n/// as well as the timestamp at which it exited.\npublic struct ExitStatus: Sendable {\n    /// The exit code for the process.\n    public var exitCode: Int32\n    /// The timestamp when the process exited.\n    public var exitedAt: Date\n\n    public init(exitCode: Int32) {\n        self.exitCode = exitCode\n        self.exitedAt = .now\n    }\n\n    public init(exitCode: Int32, exitedAt: Date) {\n        self.exitCode = exitCode\n        self.exitedAt = exitedAt\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/FileMount.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(macOS)\n\nimport ContainerizationError\nimport ContainerizationOCI\nimport Foundation\n\n/// Manages single-file mounts by transforming them into virtiofs directory shares\n/// plus bind mounts.\n///\n/// Since virtiofs only supports sharing directories, mounting a single file without\n/// exposing the other potential files in that directory needs a little bit of a \"hack\".\n/// The one we've landed on is:\n///\n/// 1. Creating a temporary directory containing a hardlink to the file\n/// 2. Sharing that directory via virtiofs to a holding location in the guest\n/// 3. Bind mounting the specific file from the holding location to the final destination\n///\n/// This type handles all three steps transparently.\nstruct FileMountContext: Sendable {\n    /// Metadata for a single prepared file mount.\n    struct PreparedMount: Sendable {\n        /// Original file path on host\n        let hostFilePath: String\n        /// Where the user wants the file in the container\n        let containerDestination: String\n        /// Just the filename\n        let filename: String\n        /// Temp directory containing the hardlinked file\n        let tempDirectory: URL\n        /// The virtiofs tag (hash of temp dir path). Used to find the AttachedFilesystem\n        let tag: String\n        /// Mount options from the original mount\n        let options: [String]\n        /// Where we mounted the share in the guest (set after mountHoldingDirectories)\n        var guestHoldingPath: String?\n    }\n\n    /// Prepared file mounts for this context\n    var preparedMounts: [PreparedMount]\n\n    /// The transformed mounts to pass to the VM (files replaced with directory shares)\n    private(set) var transformedMounts: [Mount]\n\n    private init() {\n        self.preparedMounts = []\n        self.transformedMounts = []\n    }\n\n    /// Returns true if there are any file mounts that need handling.\n    var hasFileMounts: Bool {\n        !preparedMounts.isEmpty\n    }\n\n    /// Returns the set of virtiofs tags for file mount holding directories.\n    /// These should be filtered out from OCI spec mounts since we mount them\n    /// separately under /run.\n    var holdingDirectoryTags: Set<String> {\n        Set(preparedMounts.map { $0.tag })\n    }\n}\n\nextension FileMountContext {\n    /// Prepare mounts for a container, detecting file mounts and transforming them.\n    ///\n    /// This method stats each virtiofs mount source. If it's a regular file rather than\n    /// a directory, it creates a temporary directory with a hardlink to the file and\n    /// substitutes a directory share for the original mount.\n    ///\n    /// - Parameter mounts: The original mounts from the container config\n    /// - Returns: A FileMountContext containing transformed mounts and tracking info\n    static func prepare(mounts: [Mount]) throws -> FileMountContext {\n        var context = FileMountContext()\n        var transformed: [Mount] = []\n\n        for mount in mounts {\n            // Only virtiofs mounts can be files\n            guard case .virtiofs(let runtimeOpts) = mount.runtimeOptions else {\n                transformed.append(mount)\n                continue\n            }\n\n            // Stat the source to see if it's a file\n            let fm = FileManager.default\n            var isDirectory: ObjCBool = false\n            guard fm.fileExists(atPath: mount.source, isDirectory: &isDirectory) else {\n                // Doesn't exist. Let the normal flow handle the error\n                transformed.append(mount)\n                continue\n            }\n\n            if isDirectory.boolValue {\n                // It's a directory, pass through unchanged\n                transformed.append(mount)\n                continue\n            }\n\n            // It's a file, so prepare it.\n            let prepared = try context.prepareFileMount(mount: mount, runtimeOptions: runtimeOpts)\n\n            // Create a regular directory share for the temp directory.\n            // The destination here is unused. We'll mount it ourselves to a location under /run.\n            let directoryShare = Mount.share(\n                source: prepared.tempDirectory.path,\n                destination: \"/.file-mount-holding\",\n                options: mount.options.filter { $0 != \"bind\" },\n                runtimeOptions: runtimeOpts\n            )\n            transformed.append(directoryShare)\n        }\n\n        context.transformedMounts = transformed\n        return context\n    }\n\n    private mutating func prepareFileMount(\n        mount: Mount,\n        runtimeOptions: [String]\n    ) throws -> PreparedMount {\n        let resolvedSource = URL(fileURLWithPath: mount.source).resolvingSymlinksInPath()\n        let sourceURL = URL(fileURLWithPath: mount.source)\n        let filename = sourceURL.lastPathComponent\n\n        let tempDir = FileManager.default.temporaryDirectory\n            .appendingPathComponent(\"containerization-file-mounts\")\n            .appendingPathComponent(UUID().uuidString)\n\n        try FileManager.default.createDirectory(\n            at: tempDir,\n            withIntermediateDirectories: true\n        )\n\n        // Hardlink the file (falls back to copy if cross-filesystem)\n        let destURL = tempDir.appendingPathComponent(filename)\n        do {\n            try FileManager.default.linkItem(at: resolvedSource, to: destURL)\n        } catch {\n            // Hardlink failed. Fall back to copy\n            try FileManager.default.copyItem(at: resolvedSource, to: destURL)\n        }\n\n        let tag = try hashMountSource(source: tempDir.path)\n\n        let prepared = PreparedMount(\n            hostFilePath: mount.source,\n            containerDestination: mount.destination,\n            filename: filename,\n            tempDirectory: tempDir,\n            tag: tag,\n            options: mount.options,\n            guestHoldingPath: nil\n        )\n\n        preparedMounts.append(prepared)\n        return prepared\n    }\n}\n\nextension FileMountContext {\n    /// Mount the holding directories in the guest for all file mounts.\n    /// - Parameters:\n    ///   - vmMounts: The AttachedFilesystem array from the VM for this container\n    ///   - agent: The VM agent for RPCs\n    mutating func mountHoldingDirectories(\n        vmMounts: [AttachedFilesystem],\n        agent: any VirtualMachineAgent\n    ) async throws {\n        for i in preparedMounts.indices {\n            let prepared = preparedMounts[i]\n\n            // Find the attached filesystem by matching the virtiofs tag\n            guard\n                let attached = vmMounts.first(where: {\n                    $0.type == \"virtiofs\" && $0.source == prepared.tag\n                })\n            else {\n                throw ContainerizationError(\n                    .notFound,\n                    message: \"could not find attached filesystem for file mount \\(prepared.hostFilePath)\"\n                )\n            }\n\n            let guestPath = \"/run/file-mounts/\\(prepared.tag)\"\n            try await agent.mkdir(path: guestPath, all: true, perms: 0o755)\n            try await agent.mount(\n                ContainerizationOCI.Mount(\n                    type: \"virtiofs\",\n                    source: attached.source,\n                    destination: guestPath,\n                    options: []\n                ))\n\n            preparedMounts[i].guestHoldingPath = guestPath\n        }\n    }\n}\n\nextension FileMountContext {\n    /// Get the bind mounts to append to the OCI spec.\n    func ociBindMounts() -> [ContainerizationOCI.Mount] {\n        preparedMounts.compactMap { prepared in\n            guard let guestPath = prepared.guestHoldingPath else {\n                return nil\n            }\n\n            return ContainerizationOCI.Mount(\n                type: \"none\",\n                source: \"\\(guestPath)/\\(prepared.filename)\",\n                destination: prepared.containerDestination,\n                options: [\"bind\"] + prepared.options\n            )\n        }\n    }\n}\n\nextension FileMountContext {\n    /// Clean up temp directories.\n    func cleanUp() {\n        let fm = FileManager.default\n        for prepared in preparedMounts {\n            try? fm.removeItem(at: prepared.tempDirectory)\n        }\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Containerization/Hash.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(macOS)\n\nimport Crypto\nimport ContainerizationError\nimport Foundation\n\npublic func hashMountSource(source: String) throws -> String {\n    // Resolve symlinks so different paths to the same directory get the same hash.\n    let resolvedSource = URL(fileURLWithPath: source).resolvingSymlinksInPath().path\n    guard let data = resolvedSource.data(using: .utf8) else {\n        throw ContainerizationError(.invalidArgument, message: \"\\(source) could not be converted to Data\")\n    }\n    return String(SHA256.hash(data: data).encoded.prefix(36))\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Containerization/HostsConfiguration.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// Static table lookups for a container. The values will be used to\n/// construct /etc/hosts for a given container.\npublic struct Hosts: Sendable {\n    /// Represents one entry in an /etc/hosts file.\n    public struct Entry: Sendable {\n        /// The IPV4 or IPV6 address in String form.\n        public var ipAddress: String\n        /// The hostname(s) for the entry.\n        public var hostnames: [String]\n        /// An optional comment to be placed to the right side of the entry.\n        public var comment: String?\n\n        public init(ipAddress: String, hostnames: [String], comment: String? = nil) {\n            self.comment = comment\n            self.hostnames = hostnames\n            self.ipAddress = ipAddress\n        }\n\n        /// The information in the structure rendered to a String representation\n        /// that matches the format /etc/hosts expects.\n        public var rendered: String {\n            var line = ipAddress\n            if !hostnames.isEmpty {\n                line += \" \" + hostnames.joined(separator: \" \")\n            }\n            if let comment {\n                line += \" # \\(comment) \"\n            }\n            return line\n        }\n\n        public static func localHostIPV4(comment: String? = nil) -> Self {\n            Self(\n                ipAddress: \"127.0.0.1\",\n                hostnames: [\"localhost\"],\n                comment: comment\n            )\n        }\n\n        public static func localHostIPV6(comment: String? = nil) -> Self {\n            Self(\n                ipAddress: \"::1\",\n                hostnames: [\"localhost\", \"ip6-localhost\", \"ip6-loopback\"],\n                comment: comment\n            )\n        }\n\n        public static func ipv6LocalNet(comment: String? = nil) -> Self {\n            Self(\n                ipAddress: \"fe00::\",\n                hostnames: [\"ip6-localnet\"],\n                comment: comment\n            )\n        }\n\n        public static func ipv6MulticastPrefix(comment: String? = nil) -> Self {\n            Self(\n                ipAddress: \"ff00::\",\n                hostnames: [\"ip6-mcastprefix\"],\n                comment: comment\n            )\n        }\n\n        public static func ipv6AllNodes(comment: String? = nil) -> Self {\n            Self(\n                ipAddress: \"ff02::1\",\n                hostnames: [\"ip6-allnodes\"],\n                comment: comment\n            )\n        }\n\n        public static func ipv6AllRouters(comment: String? = nil) -> Self {\n            Self(\n                ipAddress: \"ff02::2\",\n                hostnames: [\"ip6-allrouters\"],\n                comment: comment\n            )\n        }\n    }\n\n    /// The entries to be written to /etc/hosts.\n    public var entries: [Entry]\n\n    /// A comment to render at the top of the file.\n    public var comment: String?\n\n    public init(\n        entries: [Entry],\n        comment: String? = nil\n    ) {\n        self.entries = entries\n        self.comment = comment\n    }\n}\n\nextension Hosts {\n    /// A default entry that can be used for convenience. It contains a IPV4\n    /// and IPV6 localhost entry, as well as ipv6 localnet, ipv6 mcastprefix,\n    /// ipv6 allnodes, and ipv6 allrouters.\n    public static let `default` = Hosts(entries: [\n        Entry.localHostIPV4(),\n        Entry.localHostIPV6(),\n        Entry.ipv6LocalNet(),\n        Entry.ipv6MulticastPrefix(),\n        Entry.ipv6AllNodes(),\n        Entry.ipv6AllRouters(),\n    ])\n\n    /// Returns a string variant of the data that can be written to\n    /// /etc/hosts directly.\n    public var hostsFile: String {\n        var lines: [String] = []\n\n        if let comment {\n            lines.append(\"# \\(comment)\")\n        }\n\n        for entry in entries {\n            lines.append(entry.rendered)\n        }\n\n        return lines.joined(separator: \"\\n\") + \"\\n\"\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/IO/ReaderStream.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// A type that returns a stream of Data.\npublic protocol ReaderStream: Sendable {\n    func stream() -> AsyncStream<Data>\n}\n"
  },
  {
    "path": "Sources/Containerization/IO/Terminal+ReaderStream.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOS\nimport Foundation\n\nextension Terminal: ReaderStream {\n    public func stream() -> AsyncStream<Data> {\n        .init { cont in\n            self.handle.readabilityHandler = { handle in\n                let data = handle.availableData\n                if data.isEmpty {\n                    self.handle.readabilityHandler = nil\n                    cont.finish()\n                    return\n                }\n                cont.yield(data)\n            }\n        }\n    }\n}\n\nextension Terminal: Writer {}\n"
  },
  {
    "path": "Sources/Containerization/IO/Writer.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// A type that writes the provided Data.\npublic protocol Writer: Sendable {\n    func write(_ data: Data) throws\n    func close() throws\n}\n"
  },
  {
    "path": "Sources/Containerization/Image/Image.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport ContainerizationOCI\nimport ContainerizationOS\nimport Foundation\n\n/// Type representing an OCI container image.\npublic struct Image: Sendable {\n    private let contentStore: ContentStore\n    /// The description for the image that comprises of its name and a reference to its root descriptor.\n    public let description: Description\n\n    /// A description of the OCI image.\n    public struct Description: Sendable {\n        /// The string reference of the image.\n        public let reference: String\n        /// The descriptor identifying the image.\n        public let descriptor: Descriptor\n        /// The digest for the image.\n        public var digest: String { descriptor.digest }\n        /// The media type of the image.\n        public var mediaType: String { descriptor.mediaType }\n\n        public init(reference: String, descriptor: Descriptor) {\n            self.reference = reference\n            self.descriptor = descriptor\n        }\n    }\n\n    /// The descriptor for the image.\n    public var descriptor: Descriptor { description.descriptor }\n    /// The digest of the image.\n    public var digest: String { description.digest }\n    /// The media type of the image.\n    public var mediaType: String { description.mediaType }\n    /// The string reference for the image.\n    public var reference: String { description.reference }\n\n    public init(description: Description, contentStore: ContentStore) {\n        self.description = description\n        self.contentStore = contentStore\n    }\n\n    /// Returns the underlying OCI index for the image.\n    public func index() async throws -> Index {\n        guard let content: Content = try await contentStore.get(digest: digest) else {\n            throw ContainerizationError(.notFound, message: \"content with digest \\(digest)\")\n        }\n        return try content.decode()\n    }\n\n    /// Returns the manifest for the specified platform.\n    public func manifest(for platform: Platform) async throws -> Manifest {\n        let index = try await self.index()\n        let desc = index.manifests.first { desc in\n            desc.platform == platform\n        }\n        guard let desc else {\n            throw ContainerizationError(.unsupported, message: \"platform \\(platform.description)\")\n        }\n        guard let content: Content = try await contentStore.get(digest: desc.digest) else {\n            throw ContainerizationError(.notFound, message: \"content with digest \\(digest)\")\n        }\n        return try content.decode()\n    }\n\n    /// Returns the descriptor for the given platform. If it does not exist\n    /// will throw a ContainerizationError with the code set to .invalidArgument.\n    public func descriptor(for platform: Platform) async throws -> Descriptor {\n        let index = try await self.index()\n        let desc = index.manifests.first { $0.platform == platform }\n        guard let desc else {\n            throw ContainerizationError(.invalidArgument, message: \"unsupported platform \\(platform)\")\n        }\n        return desc\n    }\n\n    /// Returns the OCI config for the specified platform.\n    public func config(for platform: Platform) async throws -> ContainerizationOCI.Image {\n        let manifest = try await self.manifest(for: platform)\n        let desc = manifest.config\n        guard let content: Content = try await contentStore.get(digest: desc.digest) else {\n            throw ContainerizationError(.notFound, message: \"content with digest \\(digest)\")\n        }\n        return try content.decode()\n    }\n\n    /// Returns a list of digests to all the referenced OCI objects.\n    public func referencedDigests() async throws -> [String] {\n        var referenced: [String] = [self.digest.trimmingDigestPrefix]\n        let index = try await self.index()\n        for manifest in index.manifests {\n            referenced.append(manifest.digest.trimmingDigestPrefix)\n            guard let m: Manifest = try? await contentStore.get(digest: manifest.digest) else {\n                // If the requested digest does not exist or is not a manifest. Skip.\n                // Its safe to skip processing this digest as it wont have any child layers.\n                continue\n            }\n            let descs = m.layers + [m.config]\n            referenced.append(contentsOf: descs.map { $0.digest.trimmingDigestPrefix })\n        }\n        return referenced\n    }\n\n    /// Returns a reference to the content blob for the image. The specified digest must be referenced by the image in one of its layers.\n    public func getContent(digest: String) async throws -> Content {\n        guard try await self.referencedDigests().contains(digest.trimmingDigestPrefix) else {\n            throw ContainerizationError(.internalError, message: \"image \\(self.reference) does not reference digest \\(digest)\")\n        }\n        guard let content: Content = try await contentStore.get(digest: digest) else {\n            throw ContainerizationError(.notFound, message: \"content with digest \\(digest)\")\n        }\n        return content\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/Image/ImageStore/ImageStore+Export.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//\n\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationIO\nimport ContainerizationOCI\nimport Crypto\nimport Foundation\n\nextension ImageStore {\n    public struct ExportOperation: Sendable {\n        let name: String\n        let tag: String\n        let contentStore: ContentStore\n        let client: ContentClient\n        let progress: ProgressHandler?\n\n        public init(name: String, tag: String, contentStore: ContentStore, client: ContentClient, progress: ProgressHandler? = nil) {\n            self.contentStore = contentStore\n            self.client = client\n            self.progress = progress\n            self.name = name\n            self.tag = tag\n        }\n\n        @discardableResult\n        public func export(index: Descriptor, platforms: (Platform) -> Bool) async throws -> Descriptor {\n            var pushQueue: [[Descriptor]] = []\n            var current: [Descriptor] = [index]\n            while !current.isEmpty {\n                let children = try await self.getChildren(descs: current)\n                let matches = try filterPlatforms(matcher: platforms, children).uniqued { $0.digest }\n                pushQueue.append(matches)\n                current = matches\n            }\n            let localIndexData = try await self.createIndex(from: index, matching: platforms)\n\n            await updatePushProgress(pushQueue: pushQueue, localIndexData: localIndexData)\n\n            // We need to work bottom up when pushing an image.\n            // First, the tar blobs / config layers, then, the manifests and so on...\n            // When processing a given \"level\", the requests maybe made in parallel.\n            // We need to ensure that the child level has been uploaded fully\n            // before uploading the parent level.\n            try await withThrowingTaskGroup(of: Void.self) { group in\n                for layerGroup in pushQueue.reversed() {\n                    for chunk in layerGroup.chunks(ofCount: 8) {\n                        for desc in chunk {\n                            guard let content = try await self.contentStore.get(digest: desc.digest) else {\n                                throw ContainerizationError(.notFound, message: \"content with digest \\(desc.digest)\")\n                            }\n                            group.addTask {\n                                let readStream = try ReadStream(url: content.path)\n                                try await self.pushContent(descriptor: desc, stream: readStream)\n                            }\n                        }\n                        try await group.waitForAll()\n                    }\n                }\n            }\n\n            // Lastly, we need to construct and push a new index, since we may\n            // have pushed content only for specific platforms.\n            let digest = SHA256.hash(data: localIndexData)\n            let descriptor = Descriptor(\n                mediaType: MediaTypes.index,\n                digest: digest.digestString,\n                size: Int64(localIndexData.count))\n            let stream = ReadStream(data: localIndexData)\n            try await self.pushContent(descriptor: descriptor, stream: stream)\n            return descriptor\n        }\n\n        private func updatePushProgress(pushQueue: [[Descriptor]], localIndexData: Data) async {\n            for layerGroup in pushQueue {\n                for desc in layerGroup {\n                    await progress?([\n                        .addTotalSize(desc.size),\n                        .addTotalItems(1),\n                    ])\n                }\n            }\n            await progress?([\n                .addTotalSize(Int64(localIndexData.count)),\n                .addTotalItems(1),\n            ])\n        }\n\n        private func createIndex(from index: Descriptor, matching: (Platform) -> Bool) async throws -> Data {\n            guard let content = try await self.contentStore.get(digest: index.digest) else {\n                throw ContainerizationError(.notFound, message: \"content with digest \\(index.digest)\")\n            }\n            var idx: Index = try content.decode()\n            let manifests = idx.manifests\n            var matchedManifests: [Descriptor] = []\n            var skippedPlatforms = false\n            for manifest in manifests {\n                guard let p = manifest.platform else {\n                    continue\n                }\n                if matching(p) {\n                    matchedManifests.append(manifest)\n                } else {\n                    skippedPlatforms = true\n                }\n            }\n            if !skippedPlatforms {\n                return try content.data()\n            }\n            idx.manifests = matchedManifests\n            return try JSONEncoder().encode(idx)\n        }\n\n        private func pushContent(descriptor: Descriptor, stream: ReadStream) async throws {\n            do {\n                let generator = {\n                    try stream.reset()\n                    return stream.stream\n                }\n                try await client.push(name: name, ref: tag, descriptor: descriptor, streamGenerator: generator, progress: progress)\n                await progress?([\n                    .addSize(descriptor.size),\n                    .addItems(1),\n                ])\n            } catch let err as ContainerizationError {\n                guard err.code != .exists else {\n                    // We reported the total items and size and have to account for them in existing content.\n                    await progress?([\n                        .addSize(descriptor.size),\n                        .addItems(1),\n                    ])\n                    return\n                }\n                throw err\n            }\n        }\n\n        private func getChildren(descs: [Descriptor]) async throws -> [Descriptor] {\n            var out: [Descriptor] = []\n            for desc in descs {\n                let mediaType = desc.mediaType\n                guard let content = try await self.contentStore.get(digest: desc.digest) else {\n                    throw ContainerizationError(.notFound, message: \"content with digest \\(desc.digest)\")\n                }\n                switch mediaType {\n                case MediaTypes.index, MediaTypes.dockerManifestList:\n                    let index: Index = try content.decode()\n                    out.append(contentsOf: index.manifests)\n                case MediaTypes.imageManifest, MediaTypes.dockerManifest:\n                    let manifest: Manifest = try content.decode()\n                    out.append(manifest.config)\n                    out.append(contentsOf: manifest.layers)\n                default:\n                    continue\n                }\n            }\n            return out\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/Image/ImageStore/ImageStore+Import.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//\n\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport Foundation\n\nextension ImageStore {\n    public struct ImportOperation: Sendable {\n        static let decoder = JSONDecoder()\n\n        let client: ContentClient\n        let ingestDir: URL\n        let contentStore: ContentStore\n        let progress: ProgressHandler?\n        let name: String\n        let maxConcurrentDownloads: Int\n\n        public init(name: String, contentStore: ContentStore, client: ContentClient, ingestDir: URL, progress: ProgressHandler? = nil, maxConcurrentDownloads: Int = 3) {\n            self.client = client\n            self.ingestDir = ingestDir\n            self.contentStore = contentStore\n            self.progress = progress\n            self.name = name\n            self.maxConcurrentDownloads = maxConcurrentDownloads\n        }\n\n        /// Pull the required image layers for the provided descriptor and platform(s) into the given directory using the provided client. Returns a descriptor to the Index manifest.\n        public func `import`(root: Descriptor, matcher: (ContainerizationOCI.Platform) -> Bool) async throws -> Descriptor {\n            var toProcess = [root]\n            while !toProcess.isEmpty {\n                // Count the total number of blobs and their size\n                if let progress {\n                    var size: Int64 = 0\n                    for desc in toProcess {\n                        size += desc.size\n                    }\n                    await progress([\n                        .addTotalSize(size),\n                        .addTotalItems(toProcess.count),\n                    ])\n                }\n\n                try await self.fetchAll(toProcess)\n                let children = try await self.walk(toProcess)\n                let filtered = try filterPlatforms(matcher: matcher, children)\n                toProcess = filtered.uniqued { $0.digest }\n            }\n\n            guard root.mediaType != MediaTypes.dockerManifestList && root.mediaType != MediaTypes.index else {\n                return root\n            }\n\n            // Create an index for the root descriptor and write it to the content store\n            let index = try await self.createIndex(for: root)\n            // In cases where the root descriptor pointed to `MediaTypes.imageManifest`\n            // Or `MediaTypes.dockerManifest`, it is required that we check the supported platform\n            // matches the platforms we were asked to pull. This can be done only after we created\n            // the Index.\n            let supportedPlatforms = index.manifests.compactMap { $0.platform }\n            guard supportedPlatforms.allSatisfy(matcher) else {\n                throw ContainerizationError(.unsupported, message: \"image \\(root.digest) does not support required platforms\")\n            }\n            let writer = try ContentWriter(for: self.ingestDir)\n            let result = try writer.create(from: index)\n            return Descriptor(\n                mediaType: MediaTypes.index,\n                digest: result.digest.digestString,\n                size: Int64(result.size))\n        }\n\n        private func getManifestContent<T: Sendable & Codable>(descriptor: Descriptor) async throws -> T {\n            do {\n                if let content = try await self.contentStore.get(digest: descriptor.digest.trimmingDigestPrefix) {\n                    return try content.decode()\n                }\n                if let content = try? LocalContent(path: ingestDir.appending(path: descriptor.digest.trimmingDigestPrefix)) {\n                    return try content.decode()\n                }\n                return try await self.client.fetch(name: name, descriptor: descriptor)\n            } catch {\n                throw ContainerizationError(.internalError, message: \"cannot fetch content with digest \\(descriptor.digest)\", cause: error)\n            }\n        }\n\n        private func walk(_ descriptors: [Descriptor]) async throws -> [Descriptor] {\n            var out: [Descriptor] = []\n            for desc in descriptors {\n                let mediaType = desc.mediaType\n                switch mediaType {\n                case MediaTypes.index, MediaTypes.dockerManifestList:\n                    let index: Index = try await self.getManifestContent(descriptor: desc)\n                    out.append(contentsOf: index.manifests)\n                case MediaTypes.imageManifest, MediaTypes.dockerManifest:\n                    let manifest: Manifest = try await self.getManifestContent(descriptor: desc)\n                    out.append(manifest.config)\n                    out.append(contentsOf: manifest.layers)\n                default:\n                    // TODO: Explicitly handle other content types\n                    continue\n                }\n            }\n            return out\n        }\n\n        private func fetchAll(_ descriptors: [Descriptor]) async throws {\n            try await withThrowingTaskGroup(of: Void.self) { group in\n                var iterator = descriptors.makeIterator()\n                // Start initial batch of concurrent downloads based on maxConcurrentDownloads\n                for _ in 0..<self.maxConcurrentDownloads {\n                    if let desc = iterator.next() {\n                        group.addTask {\n                            try await self.fetch(desc)\n                        }\n                    }\n                }\n                // As tasks complete, add new ones to maintain concurrency\n                for try await _ in group {\n                    if let desc = iterator.next() {\n                        group.addTask {\n                            try await self.fetch(desc)\n                        }\n                    }\n                }\n            }\n        }\n\n        private func fetch(_ descriptor: Descriptor) async throws {\n            if let found = try await self.contentStore.get(digest: descriptor.digest) {\n                try FileManager.default.copyItem(at: found.path, to: ingestDir.appendingPathComponent(descriptor.digest.trimmingDigestPrefix))\n                await progress?([\n                    // Count the size of the blob\n                    .addSize(descriptor.size),\n                    // Count the number of blobs\n                    .addItems(1),\n                ])\n                return\n            }\n\n            if descriptor.size > 1.mib() {\n                try await self.fetchBlob(descriptor)\n            } else {\n                try await self.fetchData(descriptor)\n            }\n            // Count the number of blobs\n            await progress?([\n                .addItems(1)\n            ])\n        }\n\n        private func fetchBlob(_ descriptor: Descriptor) async throws {\n            let id = UUID().uuidString\n            let fm = FileManager.default\n            let tempFile = ingestDir.appendingPathComponent(id)\n            let (_, digest) = try await client.fetchBlob(name: name, descriptor: descriptor, into: tempFile, progress: progress)\n            guard digest.digestString == descriptor.digest else {\n                throw ContainerizationError(.internalError, message: \"digest mismatch expected \\(descriptor.digest), got \\(digest.digestString)\")\n            }\n            do {\n                try fm.moveItem(at: tempFile, to: ingestDir.appendingPathComponent(digest.encoded))\n            } catch let err as NSError {\n                guard err.code == NSFileWriteFileExistsError else {\n                    throw err\n                }\n                try fm.removeItem(at: tempFile)\n            }\n        }\n\n        @discardableResult\n        private func fetchData(_ descriptor: Descriptor) async throws -> Data {\n            let data = try await client.fetchData(name: name, descriptor: descriptor)\n            let writer = try ContentWriter(for: ingestDir)\n            let result = try writer.write(data)\n            if let progress {\n                let size = Int64(result.size)\n                await progress([\n                    .addSize(size)\n                ])\n            }\n            guard result.digest.digestString == descriptor.digest else {\n                throw ContainerizationError(.internalError, message: \"digest mismatch expected \\(descriptor.digest), got \\(result.digest.digestString)\")\n            }\n            return data\n        }\n\n        private func createIndex(for root: Descriptor) async throws -> Index {\n            switch root.mediaType {\n            case MediaTypes.index, MediaTypes.dockerManifestList:\n                return try await self.getManifestContent(descriptor: root)\n            case MediaTypes.imageManifest, MediaTypes.dockerManifest:\n                let supportedPlatforms = try await getSupportedPlatforms(for: root)\n                guard supportedPlatforms.count == 1 else {\n                    throw ContainerizationError(\n                        .internalError,\n                        message:\n                            \"descriptor \\(root.mediaType) with digest \\(root.digest) does not list any supported platform or supports more than one platform, supported platforms: \\(supportedPlatforms)\"\n                    )\n                }\n                let platform = supportedPlatforms.first!\n                var root = root\n                root.platform = platform\n                let index = ContainerizationOCI.Index(\n                    schemaVersion: 2, manifests: [root],\n                    annotations: [\n                        // indicate that this is a synthesized index which is not directly user facing\n                        AnnotationKeys.containerizationIndexIndirect: \"true\"\n                    ])\n                return index\n            default:\n                throw ContainerizationError(.internalError, message: \"failed to create index for descriptor \\(root.digest), media type \\(root.mediaType)\")\n            }\n        }\n\n        private func getSupportedPlatforms(for root: Descriptor) async throws -> [ContainerizationOCI.Platform] {\n            var supportedPlatforms: [ContainerizationOCI.Platform] = []\n            var toProcess = [root]\n            while !toProcess.isEmpty {\n                let children = try await self.walk(toProcess)\n                for child in children {\n                    if let p = child.platform {\n                        supportedPlatforms.append(p)\n                        continue\n                    }\n                    switch child.mediaType {\n                    case MediaTypes.imageConfig, MediaTypes.dockerImageConfig:\n                        let config: ContainerizationOCI.Image = try await self.getManifestContent(descriptor: child)\n                        let p = ContainerizationOCI.Platform(\n                            arch: config.architecture, os: config.os, osFeatures: config.osFeatures, variant: config.variant\n                        )\n                        supportedPlatforms.append(p)\n                    default:\n                        continue\n                    }\n                }\n                toProcess = children\n            }\n            return supportedPlatforms\n        }\n\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/Image/ImageStore/ImageStore+OCILayout.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport Foundation\n\nextension ImageStore {\n    /// Exports the specified images and their associated layers to an OCI Image Layout directory.\n    /// This function saves the images identified by the `references` array, including their\n    /// manifests and layer blobs, into a directory structure compliant with the OCI Image Layout specification at the given `out` URL.\n    ///\n    /// - Parameters:\n    ///   - references: A list image references that exists in the `ImageStore` that are to be saved in the OCI Image Layout format.\n    ///   - out: A URL to a directory on disk at which the OCI Image Layout structure will be created.\n    ///   - platform: An optional parameter to indicate the platform to be saved for the images.\n    ///               Defaults to `nil` signifying that layers for all supported platforms by the images will be saved.\n    ///\n    public func save(references: [String], out: URL, platform: Platform? = nil) async throws {\n        let matcher = createPlatformMatcher(for: platform)\n        let fileManager = FileManager.default\n        let tempDir = fileManager.uniqueTemporaryDirectory()\n        defer {\n            try? fileManager.removeItem(at: tempDir)\n        }\n\n        var toSave: [Image] = []\n        for reference in references {\n            let image = try await self.get(reference: reference)\n            let allowedMediaTypes = [MediaTypes.dockerManifestList, MediaTypes.index]\n            guard allowedMediaTypes.contains(image.mediaType) else {\n                throw ContainerizationError(.internalError, message: \"cannot save image \\(image.reference) with Index media type \\(image.mediaType)\")\n            }\n            toSave.append(image)\n        }\n        let client = try LocalOCILayoutClient(root: out)\n        var saved: [Descriptor] = []\n\n        for image in toSave {\n            let ref = try Reference.parse(image.reference)\n            let name = ref.path\n            guard let tag = ref.tag ?? ref.digest else {\n                throw ContainerizationError(.invalidArgument, message: \"invalid tag/digest for image reference \\(image.reference)\")\n            }\n            let operation = ExportOperation(name: name, tag: tag, contentStore: self.contentStore, client: client, progress: nil)\n            var descriptor = try await operation.export(index: image.descriptor, platforms: matcher)\n            client.setImageReferenceAnnotation(descriptor: &descriptor, reference: image.reference)\n            saved.append(descriptor)\n        }\n        try client.createOCILayoutStructure(directory: out, manifests: saved)\n    }\n\n    /// Imports one or more images and their associated layers from an OCI Image Layout directory.\n    ///\n    /// - Parameters:\n    ///   - directory: A URL to a directory on disk at that follows the OCI Image Layout structure.\n    ///   - progress: An optional handler over which progress update events about the load operation can be received.\n    /// - Returns: The list of images that were loaded into the `ImageStore`.\n    ///\n    public func load(from directory: URL, progress: ProgressHandler? = nil) async throws -> [Image] {\n        let client = try LocalOCILayoutClient(root: directory)\n        let index = try client.loadIndexFromOCILayout(directory: directory)\n        let matcher = createPlatformMatcher(for: nil)\n\n        var loaded: [Image.Description] = []\n        let (id, tempDir) = try await self.contentStore.newIngestSession()\n        do {\n            for descriptor in index.manifests {\n                let reference = client.getImageReferencefromDescriptor(descriptor: descriptor)\n                let ref = try Reference.parse(reference)\n                let name = ref.path\n                let operation = ImportOperation(name: name, contentStore: self.contentStore, client: client, ingestDir: tempDir, progress: progress)\n                let indexDesc = try await operation.import(root: descriptor, matcher: matcher)\n                loaded.append(Image.Description(reference: reference, descriptor: indexDesc))\n            }\n\n            let loadedImages = loaded\n            let importedImages = try await self.lock.withLock { lock in\n                var images: [Image] = []\n                try await self.contentStore.completeIngestSession(id)\n                for description in loadedImages {\n                    let img = try await self._create(description: description, lock: lock)\n                    images.append(img)\n                }\n                return images\n            }\n            guard importedImages.count > 0 else {\n                throw ContainerizationError(.internalError, message: \"failed to import image\")\n            }\n            return importedImages\n        } catch {\n            try? await self.contentStore.cancelIngestSession(id)\n            throw error\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/Image/ImageStore/ImageStore+ReferenceManager.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport ContainerizationOCI\nimport Foundation\n\nextension ImageStore {\n    /// A ReferenceManager handles the mappings between an image's\n    /// reference and the underlying descriptor inside of a content store.\n    internal actor ReferenceManager {\n        private let path: URL\n\n        private typealias State = [String: Descriptor]\n        private var images: State\n\n        public init(path: URL) throws {\n            try FileManager.default.createDirectory(at: path, withIntermediateDirectories: true)\n\n            self.path = path\n            self.images = [:]\n        }\n\n        private func load() throws -> State {\n            let statePath = self.path.appendingPathComponent(\"state.json\")\n            guard FileManager.default.fileExists(atPath: statePath.absolutePath()) else {\n                return [:]\n            }\n            do {\n                let data = try Data(contentsOf: statePath)\n                return try JSONDecoder().decode(State.self, from: data)\n            } catch {\n                throw ContainerizationError(.internalError, message: \"failed to load image state \\(error.localizedDescription)\")\n            }\n        }\n\n        private func save(_ state: State) throws {\n            let statePath = self.path.appendingPathComponent(\"state.json\")\n            try JSONEncoder().encode(state).write(to: statePath)\n        }\n\n        public func delete(reference: String) throws {\n            var state = try self.load()\n            state.removeValue(forKey: reference)\n            try self.save(state)\n        }\n\n        public func delete(image: Image.Description) throws {\n            try self.delete(reference: image.reference)\n        }\n\n        public func create(description: Image.Description) throws {\n            var state = try self.load()\n            state[description.reference] = description.descriptor\n            try self.save(state)\n        }\n\n        public func list() throws -> [Image.Description] {\n            let state = try self.load()\n            return state.map { key, val in\n                let description = Image.Description(reference: key, descriptor: val)\n                return description\n            }\n        }\n\n        public func get(reference: String) throws -> Image.Description {\n            let images = try self.list()\n            let hit = images.first(where: { image in\n                image.reference == reference\n            })\n            guard let hit else {\n                throw ContainerizationError(.notFound, message: \"image \\(reference) not found\")\n            }\n            return hit\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/Image/ImageStore/ImageStore.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport Foundation\n\n/// An ImageStore handles the mappings between an image's\n/// reference and the underlying descriptor inside of a content store.\npublic actor ImageStore: Sendable {\n    /// The ImageStore path it was created with.\n    public nonisolated let path: URL\n\n    private let referenceManager: ReferenceManager\n    internal let contentStore: ContentStore\n    internal let lock: AsyncLock = AsyncLock()\n\n    public init(path: URL, contentStore: ContentStore? = nil) throws {\n        try FileManager.default.createDirectory(at: path, withIntermediateDirectories: true)\n\n        if let contentStore {\n            self.contentStore = contentStore\n        } else {\n            self.contentStore = try LocalContentStore(path: path.appendingPathComponent(\"content\"))\n        }\n\n        self.path = path\n        self.referenceManager = try ReferenceManager(path: path)\n    }\n\n    /// Return the default image store for the current user.\n    public static let `default`: ImageStore = {\n        do {\n            let root = try defaultRoot()\n            return try ImageStore(path: root)\n        } catch {\n            fatalError(\"unable to initialize default ImageStore \\(error)\")\n        }\n    }()\n\n    private static func defaultRoot() throws -> URL {\n        let root = FileManager.default.urls(\n            for: .applicationSupportDirectory,\n            in: .userDomainMask\n        ).first\n        guard let root else {\n            throw ContainerizationError(.notFound, message: \"unable to get Application Support directory for current user\")\n        }\n        return root.appendingPathComponent(\"com.apple.containerization\")\n\n    }\n}\n\nextension ImageStore {\n    /// Get an image from the `ImageStore`.\n    ///\n    /// - Parameters:\n    ///   - reference: Name of the image.\n    ///   - pull: Pull the image if it is not found.\n    ///\n    /// - Returns: A `Containerization.Image`  object whose `reference` matches the given string.\n    ///   This  method throws a `ContainerizationError(code: .notFound)` if the provided reference does not exist in the `ImageStore`.\n    public func get(reference: String, pull: Bool = false) async throws -> Image {\n        do {\n            let desc = try await self.referenceManager.get(reference: reference)\n            return Image(description: desc, contentStore: self.contentStore)\n        } catch let error as ContainerizationError {\n            if error.code == .notFound && pull {\n                return try await self.pull(reference: reference)\n            }\n            throw error\n        }\n    }\n\n    /// Get a list of all images in the `ImageStore`.\n    ///\n    /// - Returns: A `[Containerization.Image]` for all the images in the `ImageStore`.\n    public func list() async throws -> [Image] {\n        try await self.referenceManager.list().map { desc in\n            Image(description: desc, contentStore: self.contentStore)\n        }\n    }\n\n    /// Create a new image in the `ImageStore`.\n    ///\n    /// - Parameters:\n    ///   - description: The underlying `Image.Description` that contains information about the reference and index descriptor for the image to be created.\n    ///\n    /// - Note: It is assumed that the underlying manifests and blob layers for the image already exists in the `ContentStore` that the `ImageStore` was initialized with. This method is invoked when the `pull(...)` , `load(...)` and `tag(...)` methods are used.\n    /// - Returns: A `Containerization.Image`\n    @discardableResult\n    public func create(description: Image.Description) async throws -> Image {\n        try await self.lock.withLock { ctx in\n            try await self._create(description: description, lock: ctx)\n        }\n    }\n\n    @discardableResult\n    internal func _create(description: Image.Description, lock: AsyncLock.Context) async throws -> Image {\n        try await self.referenceManager.create(description: description)\n        return Image(description: description, contentStore: self.contentStore)\n    }\n\n    /// Delete an image from the `ImageStore`.\n    ///\n    /// - Parameters:\n    ///   - reference: Name of the image that is to be deleted.\n    ///   - performCleanup: Perform a garbage collection on the `ContentStore`, removing all unreferenced image layers and manifests,\n    public func delete(reference: String, performCleanup: Bool = false) async throws {\n        try await self.lock.withLock { lockCtx in\n            try await self.referenceManager.delete(reference: reference)\n            if performCleanup {\n                try await self._cleanUpOrphanedBlobs(lockCtx)\n            }\n        }\n    }\n\n    /// Clean up orphaned blobs that are no longer referenced by any image.\n    ///\n    /// - Returns: Returns a tuple of `(deleted, freed)`.\n    ///   `deleted` :  A  list of the names of the content items that were deleted from the `ContentStore`,\n    ///   `freed` : The total size of the items that were deleted.\n    @discardableResult\n    public func cleanUpOrphanedBlobs() async throws -> (deleted: [String], freed: UInt64) {\n        try await self.lock.withLock { lockCtx in\n            try await self._cleanUpOrphanedBlobs(lockCtx)\n        }\n    }\n\n    /// Calculate the size of orphaned blobs without deleting them.\n    ///\n    /// - Returns: The total size in bytes of blobs that are not referenced by any image.\n    public func calculateOrphanedBlobsSize() async throws -> UInt64 {\n        try await self.lock.withLock { lockCtx in\n            try await self._calculateOrphanedBlobsSize(lockCtx)\n        }\n    }\n\n    @discardableResult\n    private func _cleanUpOrphanedBlobs(_ lock: AsyncLock.Context) async throws -> (deleted: [String], freed: UInt64) {\n        let images = try await self.list()\n        var referenced: [String] = []\n        for image in images {\n            try await referenced.append(contentsOf: image.referencedDigests().uniqued())\n        }\n        let (deleted, size) = try await self.contentStore.delete(keeping: referenced)\n        return (deleted, size)\n    }\n\n    private func _calculateOrphanedBlobsSize(_ lock: AsyncLock.Context) async throws -> UInt64 {\n        let images = try await self.list()\n        var referenced: [String] = []\n        for image in images {\n            try await referenced.append(contentsOf: image.referencedDigests().uniqued())\n        }\n\n        // Calculate size of blobs not in the referenced list\n        let referencedSet = Set(referenced.map { $0.trimmingDigestPrefix })\n        let blobsPath = self.path.appendingPathComponent(\"content/blobs/sha256\")\n\n        let fileManager = FileManager.default\n        let allBlobs = try fileManager.contentsOfDirectory(\n            at: blobsPath,\n            includingPropertiesForKeys: [.fileSizeKey],\n            options: [.skipsHiddenFiles]\n        )\n\n        var orphanedSize: UInt64 = 0\n        for blobURL in allBlobs {\n            let digest = blobURL.lastPathComponent\n            if !referencedSet.contains(digest) {\n                if let resourceValues = try? blobURL.resourceValues(forKeys: [.fileSizeKey]),\n                    let size = resourceValues.fileSize\n                {\n                    orphanedSize += UInt64(size)\n                }\n            }\n        }\n\n        return orphanedSize\n    }\n\n    /// Tag an existing image such that it can be referenced by another name.\n    ///\n    /// - Parameters:\n    ///   - existing: The reference to an image that already exists in the `ImageStore`.\n    ///   - new: The new reference by which the image should also be referenced as.\n    /// - Note: The new image created in the `ImageStore` will have the same `Image.Description`\n    ///         as that of the image with reference `existing.`\n    /// - Returns: A `Containerization.Image` object to the newly created image.\n    public func tag(existing: String, new: String) async throws -> Image {\n        let old = try await self.get(reference: existing)\n        let descriptor = old.descriptor\n        do {\n            _ = try Reference.parse(new)\n        } catch {\n            throw ContainerizationError(.invalidArgument, message: \"invalid reference \\(new), error: \\(error)\")\n        }\n        let newDescription = Image.Description(reference: new, descriptor: descriptor)\n        return try await self.create(description: newDescription)\n    }\n}\n\nextension ImageStore {\n    /// Pull an image and its associated manifest and blob layers from a remote registry.\n    ///\n    /// - Parameters:\n    ///   - reference: A string that references an image in a remote registry of the form `<host>[:<port>]/repository:<tag>`\n    ///                For example: \"docker.io/library/alpine:latest\".\n    ///   - platform: An optional parameter to indicate the platform to be pulled for the image.\n    ///               Defaults to `nil` signifying that layers for all supported platforms by the image will be pulled.\n    ///   - insecure: A boolean indicating if the connection to the remote registry should be made via plain-text http or not.\n    ///               Defaults to false, meaning the connection to the registry will be over https.\n    ///   - auth: An object that implements the `Authentication` protocol,\n    ///           used to add any credentials to the HTTP requests that are made to the registry.\n    ///           Defaults to `nil` meaning no additional credentials are added to any HTTP requests made to the registry.\n    ///   - progress: An optional handler over which progress update events about the pull operation can be received.\n    ///\n    /// - Returns: A `Containerization.Image` object to the newly pulled image.\n    public func pull(\n        reference: String, platform: Platform? = nil, insecure: Bool = false,\n        auth: Authentication? = nil, progress: ProgressHandler? = nil, maxConcurrentDownloads: Int = 3\n    ) async throws -> Image {\n\n        let matcher = createPlatformMatcher(for: platform)\n        let client = try RegistryClient(reference: reference, insecure: insecure, auth: auth, tlsConfiguration: TLSUtils.makeEnvironmentAwareTLSConfiguration())\n\n        let ref = try Reference.parse(reference)\n        let name = ref.path\n        guard let tag = ref.tag ?? ref.digest else {\n            throw ContainerizationError(.invalidArgument, message: \"invalid tag/digest for image reference \\(reference)\")\n        }\n\n        let rootDescriptor = try await client.resolve(name: name, tag: tag)\n        let (id, tempDir) = try await self.contentStore.newIngestSession()\n        let operation = ImportOperation(\n            name: name, contentStore: self.contentStore, client: client, ingestDir: tempDir, progress: progress, maxConcurrentDownloads: maxConcurrentDownloads)\n        do {\n            let index = try await operation.import(root: rootDescriptor, matcher: matcher)\n            return try await self.lock.withLock { lock in\n                try await self.contentStore.completeIngestSession(id)\n                let description = Image.Description(reference: reference, descriptor: index)\n                let image = try await self._create(description: description, lock: lock)\n                return image\n            }\n        } catch {\n            try? await self.contentStore.cancelIngestSession(id)\n            throw error\n        }\n    }\n\n    /// Push an image and its associated manifest and blob layers to a remote registry.\n    ///\n    /// - Parameters:\n    ///   - reference: A string that references an image in the `ImageStore`.  It must be of the form `<host>[:<port>]/repository:<tag>`\n    ///                For example: \"ghcr.io/foo-bar-baz/image:v1\".\n    ///   - platform: An optional parameter to indicate the platform to be pushed for the image.\n    ///               Defaults to `nil` signifying that layers for all supported platforms by the image will be pushed to the remote registry.\n    ///   - insecure: A boolean indicating if the connection to the remote registry should be made via plain-text http or not.\n    ///               Defaults to false, meaning the connection to the registry will be over https.\n    ///   - auth: An object that implements the `Authentication` protocol,\n    ///           used to add any credentials to the HTTP requests that are made to the registry.\n    ///           Defaults to `nil` meaning no additional credentials are added to any HTTP requests made to the registry.\n    ///   - progress: An optional handler over which progress update events about the push operation can be received.\n    ///\n    public func push(reference: String, platform: Platform? = nil, insecure: Bool = false, auth: Authentication? = nil, progress: ProgressHandler? = nil) async throws {\n        let matcher = createPlatformMatcher(for: platform)\n        let img = try await self.get(reference: reference)\n        let allowedMediaTypes = [MediaTypes.dockerManifestList, MediaTypes.index]\n        guard allowedMediaTypes.contains(img.mediaType) else {\n            throw ContainerizationError(.internalError, message: \"cannot push image \\(reference) with Index media type \\(img.mediaType)\")\n        }\n        let ref = try Reference.parse(reference)\n        let name = ref.path\n        guard let tag = ref.tag ?? ref.digest else {\n            throw ContainerizationError(.invalidArgument, message: \"invalid tag/digest for image reference \\(reference)\")\n        }\n        let client = try RegistryClient(reference: reference, insecure: insecure, auth: auth, tlsConfiguration: TLSUtils.makeEnvironmentAwareTLSConfiguration())\n        let operation = ExportOperation(name: name, tag: tag, contentStore: self.contentStore, client: client, progress: progress)\n        try await operation.export(index: img.descriptor, platforms: matcher)\n    }\n}\n\nextension ImageStore {\n    /// Get the image for the init block from the image store.\n    /// If the image does not exist locally, pull the image.\n    public func getInitImage(reference: String, auth: Authentication? = nil, progress: ProgressHandler? = nil) async throws -> InitImage {\n        do {\n            let image = try await self.get(reference: reference)\n            return InitImage(image: image)\n        } catch let error as ContainerizationError {\n            if error.code == .notFound {\n                let image = try await self.pull(reference: reference, auth: auth, progress: progress)\n                return InitImage(image: image)\n            }\n            throw error\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/Image/InitImage.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport ContainerizationOCI\nimport Foundation\n\n/// Data representing the image to use as the root filesystem for a virtual machine.\n/// Typically this image would contain the guest agent used to facilitate container\n/// workloads, as well as any extras that may be useful to have in the guest.\npublic struct InitImage: Sendable {\n    public var name: String { image.reference }\n\n    let image: Image\n\n    public init(image: Image) {\n        self.image = image\n    }\n}\n\nextension InitImage {\n    /// Unpack the initial filesystem for the desired platform at a given path.\n    public func initBlock(at: URL, for platform: SystemPlatform) async throws -> Mount {\n        let unpacker = EXT4Unpacker(blockSizeInBytes: 512.mib())\n        var fs = try await unpacker.unpack(self.image, for: platform.ociPlatform(), at: at)\n        fs.options = [\"ro\"]\n        return fs\n    }\n\n    /// Create a new InitImage with the reference as the name.\n    /// The `rootfs` parameter must be a tar.gz file whose contents make up the filesystem for the image.\n    public static func create(\n        reference: String, rootfs: URL, platform: Platform,\n        labels: [String: String] = [:], imageStore: ImageStore, contentStore: ContentStore\n    ) async throws -> InitImage {\n\n        let indexDescriptorStore = AsyncStore<Descriptor>()\n        try await contentStore.ingest { dir in\n            let writer = try ContentWriter(for: dir)\n            var result = try writer.create(from: rootfs)\n            let layerDescriptor = Descriptor(mediaType: ContainerizationOCI.MediaTypes.imageLayerGzip, digest: result.digest.digestString, size: result.size)\n\n            // TODO: compute and fill in the correct diffID for the above layer\n            // We currently put in the sha of the fully compressed layer, this needs to be replaced with\n            // the sha of the uncompressed layer.\n            let rootfsConfig = ContainerizationOCI.Rootfs(type: \"layers\", diffIDs: [result.digest.digestString])\n            let runtimeConfig = ContainerizationOCI.ImageConfig(labels: labels)\n            let imageConfig = ContainerizationOCI.Image(architecture: platform.architecture, os: platform.os, config: runtimeConfig, rootfs: rootfsConfig)\n            result = try writer.create(from: imageConfig)\n            let configDescriptor = Descriptor(mediaType: ContainerizationOCI.MediaTypes.imageConfig, digest: result.digest.digestString, size: result.size)\n\n            let manifest = Manifest(config: configDescriptor, layers: [layerDescriptor])\n            result = try writer.create(from: manifest)\n            let manifestDescriptor = Descriptor(mediaType: ContainerizationOCI.MediaTypes.imageManifest, digest: result.digest.digestString, size: result.size, platform: platform)\n\n            let index = ContainerizationOCI.Index(manifests: [manifestDescriptor])\n            result = try writer.create(from: index)\n\n            let indexDescriptor = Descriptor(mediaType: ContainerizationOCI.MediaTypes.index, digest: result.digest.digestString, size: result.size)\n            await indexDescriptorStore.set(indexDescriptor)\n\n        }\n\n        guard let indexDescriptor = await indexDescriptorStore.get() else {\n            throw ContainerizationError(.notFound, message: \"image for \\(reference) not found\")\n        }\n\n        let description = Image.Description(reference: reference, descriptor: indexDescriptor)\n        let image = try await imageStore.create(description: description)\n        return InitImage(image: image)\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/Image/KernelImage.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport ContainerizationOCI\nimport Foundation\n\n/// A multi-arch kernel image represented by an OCI image.\npublic struct KernelImage: Sendable {\n    /// The media type for a kernel image.\n    public static let mediaType = \"application/vnd.apple.containerization.kernel\"\n\n    /// The name or reference of the image.\n    public var name: String { image.reference }\n\n    let image: Image\n\n    public init(image: Image) {\n        self.image = image\n    }\n}\n\nextension KernelImage {\n    /// Return the kernel from a multi arch image for a specific system platform.\n    public func kernel(for platform: SystemPlatform) async throws -> Kernel {\n        let manifest = try await image.manifest(for: platform.ociPlatform())\n        guard let descriptor = manifest.layers.first, descriptor.mediaType == Self.mediaType else {\n            throw ContainerizationError(.notFound, message: \"kernel descriptor for \\(platform) not found\")\n        }\n        let content = try await image.getContent(digest: descriptor.digest)\n        return Kernel(\n            path: content.path,\n            platform: platform\n        )\n    }\n\n    /// Create a new kernel image with the reference as the name.\n    /// This will create a multi arch image containing kernel's for each provided architecture.\n    public static func create(reference: String, binaries: [Kernel], labels: [String: String] = [:], imageStore: ImageStore, contentStore: ContentStore) async throws -> KernelImage\n    {\n        let indexDescriptorStore = AsyncStore<Descriptor>()\n        try await contentStore.ingest { ingestPath in\n            var descriptors = [Descriptor]()\n            let writer = try ContentWriter(for: ingestPath)\n\n            for kernel in binaries {\n                var result = try writer.create(from: kernel.path)\n                let platform = kernel.platform.ociPlatform()\n                let layerDescriptor = Descriptor(\n                    mediaType: mediaType,\n                    digest: result.digest.digestString,\n                    size: result.size,\n                    platform: platform)\n                let rootfsConfig = ContainerizationOCI.Rootfs(type: \"layers\", diffIDs: [result.digest.digestString])\n                let runtimeConfig = ContainerizationOCI.ImageConfig(labels: labels)\n                let imageConfig = ContainerizationOCI.Image(architecture: platform.architecture, os: platform.os, config: runtimeConfig, rootfs: rootfsConfig)\n\n                result = try writer.create(from: imageConfig)\n                let configDescriptor = Descriptor(mediaType: ContainerizationOCI.MediaTypes.imageConfig, digest: result.digest.digestString, size: result.size)\n\n                let manifest = Manifest(config: configDescriptor, layers: [layerDescriptor])\n                result = try writer.create(from: manifest)\n                let manifestDescriptor = Descriptor(\n                    mediaType: ContainerizationOCI.MediaTypes.imageManifest, digest: result.digest.digestString, size: result.size, platform: platform)\n                descriptors.append(manifestDescriptor)\n            }\n            let index = ContainerizationOCI.Index(manifests: descriptors)\n            let result = try writer.create(from: index)\n            let indexDescriptor = Descriptor(mediaType: ContainerizationOCI.MediaTypes.index, digest: result.digest.digestString, size: result.size)\n            await indexDescriptorStore.set(indexDescriptor)\n        }\n\n        guard let indexDescriptor = await indexDescriptorStore.get() else {\n            throw ContainerizationError(.notFound, message: \"image for \\(reference) not found\")\n        }\n\n        let description = Image.Description(reference: reference, descriptor: indexDescriptor)\n        let image = try await imageStore.create(description: description)\n        return KernelImage(image: image)\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/Image/Unpacker/EXT4Unpacker.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport Foundation\n\n#if os(macOS)\nimport ContainerizationArchive\nimport ContainerizationEXT4\nimport SystemPackage\n#endif\n\npublic struct EXT4Unpacker: Unpacker {\n    let blockSizeInBytes: UInt64\n\n    public init(blockSizeInBytes: UInt64) {\n        self.blockSizeInBytes = blockSizeInBytes\n    }\n\n    #if os(macOS)\n    /// Performs the unpacking of a tar archive into a filesystem.\n    /// - Parameters:\n    ///   - archive: The archive to unpack.\n    ///   - compression: The compression to use when unpacking the image.\n    ///   - path: The path to the filesystem that will be created.\n    public func unpack(\n        archive: URL,\n        compression: ContainerizationArchive.Filter,\n        at path: URL\n    ) throws {\n        let cleanedPath = try prepareUnpackPath(path: path)\n        let filesystem = try EXT4.Formatter(\n            FilePath(cleanedPath),\n            minDiskSize: blockSizeInBytes\n        )\n        defer { try? filesystem.close() }\n\n        try filesystem.unpack(\n            source: archive,\n            format: .paxRestricted,\n            compression: compression,\n            progress: nil\n        )\n    }\n    #endif\n\n    /// Returns a `Mount` point after unpacking the image into a filesystem.\n    /// - Parameters:\n    ///   - image: The image to unpack.\n    ///   - platform: The platform content to unpack.\n    ///   - path: The path to the directory where the filesystem will be created.\n    ///   - progress: The progress handler to invoke as the unpacking progresses.\n    public func unpack(\n        _ image: Image,\n        for platform: Platform,\n        at path: URL,\n        progress: ProgressHandler? = nil\n    ) async throws -> Mount {\n        #if !os(macOS)\n        throw ContainerizationError(.unsupported, message: \"cannot unpack an image on current platform\")\n        #else\n        let cleanedPath = try prepareUnpackPath(path: path)\n        let manifest = try await image.manifest(for: platform)\n        let filesystem = try EXT4.Formatter(\n            FilePath(\n                cleanedPath\n            ),\n            minDiskSize: blockSizeInBytes\n        )\n        defer { try? filesystem.close() }\n\n        for layer in manifest.layers {\n            try Task.checkCancellation()\n            let content = try await image.getContent(digest: layer.digest)\n\n            let compression: ContainerizationArchive.Filter\n            switch layer.mediaType {\n            case MediaTypes.imageLayer, MediaTypes.dockerImageLayer:\n                compression = .none\n            case MediaTypes.imageLayerGzip, MediaTypes.dockerImageLayerGzip:\n                compression = .gzip\n            case MediaTypes.imageLayerZstd, MediaTypes.dockerImageLayerZstd:\n                compression = .zstd\n            default:\n                throw ContainerizationError(.unsupported, message: \"media type \\(layer.mediaType) not supported.\")\n            }\n            try filesystem.unpack(\n                source: content.path,\n                format: .paxRestricted,\n                compression: compression,\n                progress: progress\n            )\n        }\n\n        return .block(\n            format: \"ext4\",\n            source: cleanedPath,\n            destination: \"/\",\n            options: []\n        )\n        #endif\n    }\n\n    private func prepareUnpackPath(path: URL) throws -> String {\n        let blockPath = path.absolutePath()\n        guard !FileManager.default.fileExists(atPath: blockPath) else {\n            throw ContainerizationError(.exists, message: \"block device already exists at \\(blockPath)\")\n        }\n        return blockPath\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/Image/Unpacker/Unpacker.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport Foundation\n\n/// The `Unpacker` protocol defines a standardized interface that involves\n/// decompressing, extracting image layers and preparing it for use.\n///\n/// The `Unpacker` is responsible for managing the lifecycle of the\n/// unpacking process, including any temporary files or resources, until the\n/// `Mount` object is produced.\npublic protocol Unpacker {\n\n    /// Unpacks the provided image to a specified path for a given platform.\n    ///\n    /// This asynchronous method should handle the entire unpacking process, from reading\n    /// the `Image` layers for the given `Platform` via its `Manifest`,\n    /// to making the extracted contents available as a `Mount`.\n    /// Implementations of this method may apply platform-specific optimizations\n    /// or transformations during the unpacking.\n    ///\n    /// Progress updates can be observed via the optional `progress` handler.\n    func unpack(_ image: Image, for platform: Platform, at path: URL, progress: ProgressHandler?) async throws -> Mount\n\n}\n"
  },
  {
    "path": "Sources/Containerization/Interface.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationExtras\n\n/// A network interface.\npublic protocol Interface: Sendable {\n    /// The interface IPv4 address and subnet prefix length, as a CIDR address.\n    /// Example: `192.168.64.3/24`\n    var ipv4Address: CIDRv4 { get }\n\n    /// The IP address for the default route, or nil for no default route.\n    var ipv4Gateway: IPv4Address? { get }\n\n    /// The interface MAC address, or nil to auto-configure the address.\n    var macAddress: MACAddress? { get }\n\n    /// The interface MTU (Maximum Transmission Unit).\n    var mtu: UInt32 { get }\n}\n\nextension Interface {\n    public var mtu: UInt32 { 1500 }\n}\n"
  },
  {
    "path": "Sources/Containerization/Kernel.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// An object representing a Linux kernel used to boot a virtual machine.\n/// In addition to a path to the kernel itself, this type stores relevant\n/// data such as the commandline to pass to the kernel, and init arguments.\npublic struct Kernel: Sendable, Codable {\n    /// The command line arguments passed to the kernel on boot.\n    public struct CommandLine: Sendable, Codable {\n        public static let kernelDefaults = [\n            \"console=hvc0\",\n            \"tsc=reliable\",\n        ]\n\n        /// Adds the debug argument to the kernel commandline.\n        mutating public func addDebug() {\n            self.kernelArgs.append(\"debug\")\n        }\n\n        /// Adds a panic level to the kernel commandline.\n        mutating public func addPanic(level: Int) {\n            self.kernelArgs.append(\"panic=\\(level)\")\n        }\n\n        /// Additional kernel arguments.\n        public var kernelArgs: [String]\n        /// Additional arguments passed to the Initial Process / Agent.\n        public var initArgs: [String]\n\n        /// Initializes the kernel commandline using the mix of kernel arguments\n        /// and init arguments.\n        public init(\n            kernelArgs: [String] = kernelDefaults,\n            initArgs: [String] = []\n        ) {\n            self.kernelArgs = kernelArgs\n            self.initArgs = initArgs\n        }\n\n        /// Initializes the kernel commandline to the defaults of Self.kernelDefaults,\n        /// adds a debug and panic flag as instructed, and optionally a set of init\n        /// process flags to supply to vminitd.\n        public init(debug: Bool, panic: Int, initArgs: [String] = []) {\n            var args = Self.kernelDefaults\n            if debug {\n                args.append(\"debug\")\n            }\n            args.append(\"panic=\\(panic)\")\n            self.kernelArgs = args\n            self.initArgs = initArgs\n        }\n    }\n\n    /// Path on disk to the kernel binary.\n    public var path: URL\n    /// Platform for the kernel.\n    public var platform: SystemPlatform\n    /// Kernel and init process command line.\n    public var commandLine: Self.CommandLine\n\n    /// Kernel command line arguments.\n    public var kernelArgs: [String] {\n        self.commandLine.kernelArgs\n    }\n\n    /// Init process arguments.\n    public var initArgs: [String] {\n        self.commandLine.initArgs\n    }\n\n    public init(\n        path: URL,\n        platform: SystemPlatform,\n        commandline: Self.CommandLine = CommandLine(debug: false, panic: 0)\n    ) {\n        self.path = path\n        self.platform = platform\n        self.commandLine = commandline\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/LinuxContainer.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(macOS)\nimport ContainerizationArchive\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport Foundation\nimport Logging\nimport Synchronization\n\nimport struct ContainerizationOS.Terminal\n\n/// `LinuxContainer` is an easy to use type for launching and managing the\n/// full lifecycle of a Linux container ran inside of a virtual machine.\npublic final class LinuxContainer: Container, Sendable {\n    /// The identifier of the container.\n    public let id: String\n\n    /// Rootfs for the container.\n    ///\n    /// Note: The `destination` field of this mount is ignored as mounting is handled internally.\n    public let rootfs: Mount\n\n    /// Optional writable layer for the container. When provided, the rootfs\n    /// is mounted as the lower layer of an overlayfs, with this as the upper layer.\n    /// All writes will go to this layer instead of the rootfs.\n    ///\n    /// Note: The `destination` field of this mount is ignored as mounting is handled internally.\n    public let writableLayer: Mount?\n\n    /// Configuration for the container.\n    public let config: Configuration\n\n    /// The configuration for the LinuxContainer.\n    public struct Configuration: Sendable {\n        /// Configuration for the init process of the container.\n        public var process = LinuxProcessConfiguration()\n        /// The amount of cpus for the container.\n        public var cpus: Int = 4\n        /// The memory in bytes to give to the container.\n        public var memoryInBytes: UInt64 = 1024.mib()\n        /// The hostname for the container.\n        public var hostname: String?\n        /// The system control options for the container.\n        public var sysctl: [String: String] = [:]\n        /// The network interfaces for the container.\n        public var interfaces: [any Interface] = []\n        /// The Unix domain socket relays to setup for the container.\n        public var sockets: [UnixSocketConfiguration] = []\n        /// The mounts for the container.\n        public var mounts: [Mount] = LinuxContainer.defaultMounts()\n        /// The DNS configuration for the container.\n        public var dns: DNS?\n        /// The hosts to add to /etc/hosts for the container.\n        public var hosts: Hosts?\n        /// Enable nested virtualization support.\n        public var virtualization: Bool = false\n        /// Optional destination for serial boot logs.\n        public var bootLog: BootLog?\n        /// EXPERIMENTAL: Path in the root filesystem for the virtual\n        /// machine where the OCI runtime used to spawn the container lives.\n        public var ociRuntimePath: String?\n        /// Run the container with a minimal init process that handles signal\n        /// forwarding and zombie reaping.\n        public var useInit: Bool = false\n\n        public init() {}\n\n        public init(\n            process: LinuxProcessConfiguration,\n            cpus: Int = 4,\n            memoryInBytes: UInt64 = 1024.mib(),\n            hostname: String? = nil,\n            sysctl: [String: String] = [:],\n            interfaces: [any Interface] = [],\n            sockets: [UnixSocketConfiguration] = [],\n            mounts: [Mount] = LinuxContainer.defaultMounts(),\n            dns: DNS? = nil,\n            hosts: Hosts? = nil,\n            virtualization: Bool = false,\n            bootLog: BootLog? = nil,\n            ociRuntimePath: String? = nil,\n            useInit: Bool = false\n        ) {\n            self.process = process\n            self.cpus = cpus\n            self.memoryInBytes = memoryInBytes\n            self.hostname = hostname\n            self.sysctl = sysctl\n            self.interfaces = interfaces\n            self.sockets = sockets\n            self.mounts = mounts\n            self.dns = dns\n            self.hosts = hosts\n            self.virtualization = virtualization\n            self.bootLog = bootLog\n            self.ociRuntimePath = ociRuntimePath\n            self.useInit = useInit\n        }\n    }\n\n    private let state: AsyncMutex<State>\n\n    // Ports to be allocated from for stdio and for\n    // unix socket relays that are sharing a guest\n    // uds to the host.\n    private let hostVsockPorts: Atomic<UInt32>\n    // Ports we request the guest to allocate for unix socket relays from\n    // the host.\n    private let guestVsockPorts: Atomic<UInt32>\n\n    // Queue for copy IO.\n    private let copyQueue = DispatchQueue(label: \"com.apple.containerization.copy\")\n\n    private enum State: Sendable {\n        /// The container class has been created but no live resources are running.\n        case initialized\n        /// The container's virtual machine has been setup and the runtime environment has been configured.\n        case created(CreatedState)\n        /// The initial process of the container has started and is running.\n        case started(StartedState)\n        /// The container has run and fully stopped.\n        case stopped\n        /// An error occurred during the lifetime of this class.\n        case errored(Swift.Error)\n        /// The container is paused.\n        case paused(PausedState)\n\n        struct CreatedState: Sendable {\n            let vm: any VirtualMachineInstance\n            let relayManager: UnixSocketRelayManager\n            var fileMountContext: FileMountContext\n        }\n\n        struct StartedState: Sendable {\n            let vm: any VirtualMachineInstance\n            let process: LinuxProcess\n            let relayManager: UnixSocketRelayManager\n            var vendedProcesses: [String: LinuxProcess]\n            let fileMountContext: FileMountContext\n\n            init(_ state: CreatedState, process: LinuxProcess) {\n                self.vm = state.vm\n                self.relayManager = state.relayManager\n                self.process = process\n                self.vendedProcesses = [:]\n                self.fileMountContext = state.fileMountContext\n            }\n\n            init(_ state: PausedState) {\n                self.vm = state.vm\n                self.relayManager = state.relayManager\n                self.process = state.process\n                self.vendedProcesses = state.vendedProcesses\n                self.fileMountContext = state.fileMountContext\n            }\n        }\n\n        struct PausedState: Sendable {\n            let vm: any VirtualMachineInstance\n            let relayManager: UnixSocketRelayManager\n            let process: LinuxProcess\n            var vendedProcesses: [String: LinuxProcess]\n            let fileMountContext: FileMountContext\n\n            init(_ state: StartedState) {\n                self.vm = state.vm\n                self.relayManager = state.relayManager\n                self.process = state.process\n                self.vendedProcesses = state.vendedProcesses\n                self.fileMountContext = state.fileMountContext\n            }\n        }\n\n        func createdState(_ operation: String) throws -> CreatedState {\n            switch self {\n            case .created(let state):\n                return state\n            case .errored(let err):\n                throw err\n            default:\n                throw ContainerizationError(\n                    .invalidState,\n                    message: \"failed to \\(operation): container must be created\"\n                )\n            }\n        }\n\n        func startedState(_ operation: String) throws -> StartedState {\n            switch self {\n            case .started(let state):\n                return state\n            case .errored(let err):\n                throw err\n            default:\n                throw ContainerizationError(\n                    .invalidState,\n                    message: \"failed to \\(operation): container must be running\"\n                )\n            }\n        }\n\n        func pausedState(_ operation: String) throws -> PausedState {\n            switch self {\n            case .paused(let state):\n                return state\n            case .errored(let err):\n                throw err\n            default:\n                throw ContainerizationError(\n                    .invalidState,\n                    message: \"failed to \\(operation): container must be paused\"\n                )\n            }\n        }\n\n        mutating func validateForCreate() throws {\n            switch self {\n            case .initialized, .stopped:\n                break\n            case .errored(let err):\n                throw err\n            default:\n                throw ContainerizationError(\n                    .invalidState,\n                    message: \"container must be in initialized or stopped state to create\"\n                )\n            }\n        }\n\n        mutating func setErrored(error: Swift.Error) {\n            self = .errored(error)\n        }\n    }\n\n    private let vmm: VirtualMachineManager\n    private let logger: Logger?\n\n    /// Create a new `LinuxContainer`.\n    ///\n    /// - Parameters:\n    ///   - id: The identifier for the container.\n    ///   - rootfs: The root filesystem mount containing the container image contents.\n    ///     The `destination` field is ignored as mounting is handled internally.\n    ///   - writableLayer: Optional writable layer mount. When provided, an overlayfs is used with\n    ///     rootfs as the lower layer and this as the upper layer. Must be a block device.\n    ///     The `destination` field is ignored as mounting is handled internally.\n    ///   - vmm: The virtual machine manager that will handle launching the VM for the container.\n    ///   - logger: Optional logger for container operations.\n    ///   - configuration: A closure that configures the container by modifying the Configuration instance.\n    public convenience init(\n        _ id: String,\n        rootfs: Mount,\n        writableLayer: Mount? = nil,\n        vmm: VirtualMachineManager,\n        logger: Logger? = nil,\n        configuration: (inout Configuration) throws -> Void\n    ) throws {\n        var config = Configuration()\n        try configuration(&config)\n        try self.init(\n            id,\n            rootfs: rootfs,\n            writableLayer: writableLayer,\n            vmm: vmm,\n            configuration: config,\n            logger: logger\n        )\n    }\n\n    /// Create a new `LinuxContainer`.\n    ///\n    /// - Parameters:\n    ///   - id: The identifier for the container.\n    ///   - rootfs: The root filesystem mount containing the container image contents.\n    ///     The `destination` field is ignored as mounting is handled internally.\n    ///   - writableLayer: Optional writable layer mount. When provided, an overlayfs is used with\n    ///     rootfs as the lower layer and this as the upper layer. Must be a block device.\n    ///     The `destination` field is ignored as mounting is handled internally.\n    ///   - vmm: The virtual machine manager that will handle launching the VM for the container.\n    ///   - configuration: The container configuration specifying process, resources, networking, and other settings.\n    ///   - logger: Optional logger for container operations.\n    public init(\n        _ id: String,\n        rootfs: Mount,\n        writableLayer: Mount? = nil,\n        vmm: VirtualMachineManager,\n        configuration: LinuxContainer.Configuration,\n        logger: Logger? = nil\n    ) throws {\n        if let writableLayer {\n            guard writableLayer.isBlock else {\n                throw ContainerizationError(\n                    .invalidArgument,\n                    message: \"writableLayer must be a block device\"\n                )\n            }\n        }\n        self.id = id\n        self.vmm = vmm\n        self.hostVsockPorts = Atomic<UInt32>(0x1000_0000)\n        self.guestVsockPorts = Atomic<UInt32>(0x1000_0000)\n        self.logger = logger\n        self.config = configuration\n        self.state = AsyncMutex(.initialized)\n        self.rootfs = rootfs\n        self.writableLayer = writableLayer\n    }\n\n    private static func createDefaultRuntimeSpec(_ id: String) -> Spec {\n        .init(\n            process: .init(),\n            hostname: id,\n            root: .init(\n                path: Self.guestRootfsPath(id),\n                readonly: false\n            ),\n            linux: .init(\n                resources: .init(),\n                cgroupsPath: \"/container/\\(id)\"\n            )\n        )\n    }\n\n    private func generateRuntimeSpec() -> Spec {\n        var spec = Self.createDefaultRuntimeSpec(id)\n\n        // Process toggles.\n        spec.process = config.process.toOCI()\n\n        // Wrap with init process if requested.\n        if config.useInit {\n            let originalArgs = spec.process?.args ?? []\n            spec.process?.args = [\"/.cz-init\", \"--\"] + originalArgs\n        }\n\n        // General toggles.\n        if let hostname = config.hostname {\n            spec.hostname = hostname\n        }\n\n        // Linux toggles.\n        spec.linux?.sysctl = config.sysctl\n\n        // If the rootfs was requested as read-only, set it in the OCI spec.\n        // We let the OCI runtime remount as ro, instead of doing it originally.\n        // However, if we have a writable layer, the overlay allows writes so we don't mark it read-only.\n        spec.root?.readonly = self.rootfs.options.contains(\"ro\") && self.writableLayer == nil\n\n        // Resource limits.\n        // CPU: quota/period model where period is 100ms (100,000µs) and quota is cpus * period\n        // Memory: limit in bytes\n        spec.linux?.resources = LinuxResources(\n            memory: LinuxMemory(\n                limit: Int64(config.memoryInBytes)\n            ),\n            cpu: LinuxCPU(\n                quota: Int64(config.cpus * 100_000),\n                period: 100_000\n            )\n        )\n\n        spec.linux?.namespaces = [\n            LinuxNamespace(type: .cgroup),\n            LinuxNamespace(type: .ipc),\n            LinuxNamespace(type: .mount),\n            LinuxNamespace(type: .pid),\n            LinuxNamespace(type: .uts),\n        ]\n\n        return spec\n    }\n\n    /// The default set of mounts for a LinuxContainer.\n    public static func defaultMounts() -> [Mount] {\n        let defaultOptions = [\"nosuid\", \"noexec\", \"nodev\"]\n        return [\n            .any(type: \"proc\", source: \"proc\", destination: \"/proc\"),\n            .any(type: \"sysfs\", source: \"sysfs\", destination: \"/sys\", options: defaultOptions),\n            .any(type: \"devtmpfs\", source: \"none\", destination: \"/dev\", options: [\"nosuid\", \"mode=755\"]),\n            .any(type: \"mqueue\", source: \"mqueue\", destination: \"/dev/mqueue\", options: defaultOptions),\n            .any(type: \"tmpfs\", source: \"tmpfs\", destination: \"/dev/shm\", options: defaultOptions + [\"mode=1777\", \"size=65536k\"]),\n            .any(type: \"cgroup2\", source: \"none\", destination: \"/sys/fs/cgroup\", options: defaultOptions),\n            .any(type: \"devpts\", source: \"devpts\", destination: \"/dev/pts\", options: [\"nosuid\", \"noexec\", \"newinstance\", \"gid=5\", \"mode=0620\", \"ptmxmode=0666\"]),\n        ]\n    }\n\n    /// A more traditional default set of mounts that OCI runtimes typically employ.\n    public static func defaultOCIMounts() -> [Mount] {\n        let defaultOptions = [\"nosuid\", \"noexec\", \"nodev\"]\n        return [\n            .any(type: \"proc\", source: \"proc\", destination: \"/proc\"),\n            .any(type: \"tmpfs\", source: \"tmpfs\", destination: \"/dev\", options: [\"nosuid\", \"mode=755\", \"size=65536k\"]),\n            .any(type: \"devpts\", source: \"devpts\", destination: \"/dev/pts\", options: [\"nosuid\", \"noexec\", \"newinstance\", \"gid=5\", \"mode=0620\", \"ptmxmode=0666\"]),\n            .any(type: \"sysfs\", source: \"sysfs\", destination: \"/sys\", options: defaultOptions),\n            .any(type: \"mqueue\", source: \"mqueue\", destination: \"/dev/mqueue\", options: defaultOptions),\n            .any(type: \"tmpfs\", source: \"tmpfs\", destination: \"/dev/shm\", options: defaultOptions + [\"mode=1777\", \"size=65536k\"]),\n            .any(type: \"cgroup2\", source: \"none\", destination: \"/sys/fs/cgroup\", options: defaultOptions),\n        ]\n    }\n\n    private static func guestRootfsPath(_ id: String) -> String {\n        \"/run/container/\\(id)/rootfs\"\n    }\n\n    private static func guestSocketStagingPath(_ containerID: String, socketID: String) -> String {\n        \"/run/container/\\(containerID)/sockets/\\(socketID).sock\"\n    }\n}\n\nextension LinuxContainer {\n    package var root: String {\n        Self.guestRootfsPath(id)\n    }\n\n    /// Number of CPU cores allocated.\n    public var cpus: Int {\n        config.cpus\n    }\n\n    /// Amount of memory in bytes allocated for the container.\n    /// This will be aligned to a 1MB boundary if it isn't already.\n    public var memoryInBytes: UInt64 {\n        config.memoryInBytes\n    }\n\n    /// Network interfaces of the container.\n    public var interfaces: [any Interface] {\n        config.interfaces\n    }\n\n    private func mountRootfs(\n        attachments: [AttachedFilesystem],\n        rootfsPath: String,\n        agent: VirtualMachineAgent\n    ) async throws {\n        guard let rootfsAttachment = attachments.first else {\n            throw ContainerizationError(.notFound, message: \"rootfs mount not found\")\n        }\n\n        if self.writableLayer != nil {\n            // Set up overlayfs with image as lower layer and writable layer as upper.\n            guard attachments.count >= 2 else {\n                throw ContainerizationError(\n                    .notFound,\n                    message: \"writable layer mount not found\"\n                )\n            }\n            let writableAttachment = attachments[1]\n\n            let lowerPath = \"/run/container/\\(self.id)/lower\"\n            let upperMountPath = \"/run/container/\\(self.id)/upper\"\n            let upperPath = \"/run/container/\\(self.id)/upper/diff\"\n            let workPath = \"/run/container/\\(self.id)/upper/work\"\n\n            // Mount the image (lower layer) as read-only.\n            var lowerMount = rootfsAttachment.to\n            lowerMount.destination = lowerPath\n            if !lowerMount.options.contains(\"ro\") {\n                lowerMount.options.append(\"ro\")\n            }\n            try await agent.mount(lowerMount)\n\n            // Mount the writable layer.\n            var upperMount = writableAttachment.to\n            upperMount.destination = upperMountPath\n            try await agent.mount(upperMount)\n\n            // Create the upper and work directories inside the writable layer.\n            try await agent.mkdir(path: upperPath, all: true, perms: 0o755)\n            try await agent.mkdir(path: workPath, all: true, perms: 0o755)\n\n            // Mount the overlay.\n            let overlayMount = ContainerizationOCI.Mount(\n                type: \"overlay\",\n                source: \"overlay\",\n                destination: rootfsPath,\n                options: [\n                    \"lowerdir=\\(lowerPath)\",\n                    \"upperdir=\\(upperPath)\",\n                    \"workdir=\\(workPath)\",\n                ]\n            )\n            try await agent.mount(overlayMount)\n        } else {\n            // No writable layer. Mount rootfs directly.\n            var rootfs = rootfsAttachment.to\n            rootfs.destination = rootfsPath\n            try await agent.mount(rootfs)\n        }\n    }\n\n    /// Create and start the underlying container's virtual machine\n    /// and set up the runtime environment. The container's init process\n    /// is NOT running afterwards.\n    public func create() async throws {\n        try await self.state.withLock { state in\n            try state.validateForCreate()\n\n            // This is a bit of an annoyance, but because the type we use for the rootfs is simply\n            // the same Mount type we use for non-rootfs mounts, it's possible someone passed 'ro'\n            // in the options (which should be perfectly valid). However, the problem is when we go to\n            // setup /etc/hosts and /etc/resolv.conf, as we'd get EROFS if they did supply 'ro'.\n            // To remedy this, remove any \"ro\" options before passing to VZ. Having the OCI runtime\n            // remount \"ro\" (which is what we do later in the guest) is truthfully the right thing,\n            // but this bit here is just a tad awkward.\n            var modifiedRootfs = self.rootfs\n            modifiedRootfs.options.removeAll(where: { $0 == \"ro\" })\n\n            // Calculate VM memory with overhead for the guest agent.\n            // The container cgroup limit stays at the requested memory, but the VM\n            // gets an additional 50MB for the guest agent (could be higher, could be lower\n            // but this is a decent baseline for now).\n            //\n            // Clamp to system RAM if the total would exceed it as Virtualization.framework\n            // bounds us to this.\n            let guestAgentOverhead: UInt64 = 50.mib()\n            let vmMemory = min(\n                self.memoryInBytes + guestAgentOverhead,\n                ProcessInfo.processInfo.physicalMemory\n            )\n\n            // Prepare file mounts. This transforms single-file mounts into directory shares.\n            let fileMountContext = try FileMountContext.prepare(mounts: self.config.mounts)\n            // This is dumb, but alas.\n            let fileMountContextHolder = Mutex<FileMountContext>(fileMountContext)\n\n            // Build the list of mounts to attach to the VM.\n            var containerMounts = [modifiedRootfs] + fileMountContext.transformedMounts\n            if let writableLayer = self.writableLayer {\n                containerMounts.insert(writableLayer, at: 1)\n            }\n\n            let vmConfig = VMConfiguration(\n                cpus: self.cpus,\n                memoryInBytes: vmMemory,\n                interfaces: self.interfaces,\n                mountsByID: [self.id: containerMounts],\n                bootLog: self.config.bootLog,\n                nestedVirtualization: self.config.virtualization\n            )\n            let creationConfig = StandardVMConfig(configuration: vmConfig)\n            let vm = try await self.vmm.create(config: creationConfig)\n            let relayManager = UnixSocketRelayManager(vm: vm, log: self.logger)\n\n            try await vm.start()\n            do {\n                try await vm.withAgent { agent in\n                    try await agent.standardSetup()\n\n                    guard let attachments = vm.mounts[self.id] else {\n                        throw ContainerizationError(.notFound, message: \"rootfs mount not found\")\n                    }\n                    let rootfsPath = Self.guestRootfsPath(self.id)\n                    try await self.mountRootfs(attachments: attachments, rootfsPath: rootfsPath, agent: agent)\n\n                    // Mount file mount holding directories under /run.\n                    if fileMountContext.hasFileMounts {\n                        let containerMounts = vm.mounts[self.id] ?? []\n                        var ctx = fileMountContextHolder.withLock { $0 }\n                        try await ctx.mountHoldingDirectories(\n                            vmMounts: containerMounts,\n                            agent: agent\n                        )\n                        fileMountContextHolder.withLock { $0 = ctx }\n                    }\n\n                    // Start up our friendly unix socket relays.\n                    for socket in self.config.sockets {\n                        try await self.relayUnixSocket(\n                            socket: socket,\n                            relayManager: relayManager,\n                            agent: agent\n                        )\n                    }\n\n                    // For every interface asked for:\n                    // 1. Add the address requested\n                    // 2. Online the adapter\n                    // 3. If a gateway IP address is present, add the default route.\n                    for (index, i) in self.interfaces.enumerated() {\n                        let name = \"eth\\(index)\"\n                        self.logger?.debug(\"setting up interface \\(name) with address \\(i.ipv4Address)\")\n                        try await agent.addressAdd(name: name, ipv4Address: i.ipv4Address)\n                        try await agent.up(name: name, mtu: i.mtu)\n                        if let ipv4Gateway = i.ipv4Gateway {\n                            if !i.ipv4Address.contains(ipv4Gateway) {\n                                self.logger?.debug(\"gateway \\(ipv4Gateway) is outside subnet \\(i.ipv4Address), adding a route first\")\n                                try await agent.routeAddLink(name: name, dstIPv4Addr: ipv4Gateway, srcIPv4Addr: i.ipv4Address.address)\n                            }\n                            try await agent.routeAddDefault(name: name, ipv4Gateway: ipv4Gateway)\n                        }\n                    }\n\n                    // Setup /etc/resolv.conf and /etc/hosts if asked for.\n                    if let dns = self.config.dns {\n                        try await agent.configureDNS(config: dns, location: rootfsPath)\n                    }\n                    if let hosts = self.config.hosts {\n                        try await agent.configureHosts(config: hosts, location: rootfsPath)\n                    }\n\n                }\n                state = .created(.init(vm: vm, relayManager: relayManager, fileMountContext: fileMountContextHolder.withLock { $0 }))\n            } catch {\n                try? await relayManager.stopAll()\n                try? await vm.stop()\n                state.setErrored(error: error)\n                throw error\n            }\n        }\n    }\n\n    /// Start the container's initial process.\n    public func start() async throws {\n        try await self.state.withLock { state in\n            let createdState = try state.createdState(\"start\")\n\n            let agent = try await createdState.vm.dialAgent()\n            do {\n                var spec = self.generateRuntimeSpec()\n                // We don't need the rootfs (or writable layer), nor do OCI runtimes want it included.\n                // Also filter out file mount holding directories. We'll mount those separately under /run.\n                let containerMounts = createdState.vm.mounts[self.id] ?? []\n                let holdingTags = createdState.fileMountContext.holdingDirectoryTags\n                // Drop rootfs, and writable layer if present.\n                let mountsToSkip = self.writableLayer != nil ? 2 : 1\n                var mounts: [ContainerizationOCI.Mount] =\n                    containerMounts.dropFirst(mountsToSkip)\n                    .filter { !holdingTags.contains($0.source) }\n                    .map { $0.to }\n                    + createdState.fileMountContext.ociBindMounts()\n\n                // When useInit is enabled, bind mount vminitd from the VM's filesystem\n                // into the container so it can be executed.\n                if self.config.useInit {\n                    mounts.append(\n                        ContainerizationOCI.Mount(\n                            type: \"bind\",\n                            source: \"/sbin/vminitd\",\n                            destination: \"/.cz-init\",\n                            options: [\"bind\", \"ro\"]\n                        ))\n                }\n\n                // Bind mount staged sockets into the container. Sockets relayed\n                // .into the container are created in a staging directory outside\n                // the rootfs to avoid symlink traversal and mount shadowing.\n                for socket in self.config.sockets where socket.direction == .into {\n                    mounts.append(\n                        ContainerizationOCI.Mount(\n                            type: \"bind\",\n                            source: Self.guestSocketStagingPath(self.id, socketID: socket.id),\n                            destination: socket.destination.path,\n                            options: [\"bind\"]\n                        ))\n                }\n\n                spec.mounts = mounts\n\n                let stdio = IOUtil.setup(\n                    portAllocator: self.hostVsockPorts,\n                    stdin: self.config.process.stdin,\n                    stdout: self.config.process.stdout,\n                    stderr: self.config.process.stderr\n                )\n\n                let process = LinuxProcess(\n                    self.id,\n                    containerID: self.id,\n                    spec: spec,\n                    io: stdio,\n                    ociRuntimePath: self.config.ociRuntimePath,\n                    agent: agent,\n                    vm: createdState.vm,\n                    logger: self.logger\n                )\n                try await process.start()\n\n                state = .started(.init(createdState, process: process))\n            } catch {\n                try? await agent.close()\n                try? await createdState.vm.stop()\n                state.setErrored(error: error)\n                throw error\n            }\n        }\n    }\n\n    private static func setupIO(\n        portAllocator: borrowing Atomic<UInt32>,\n        stdin: ReaderStream?,\n        stdout: Writer?,\n        stderr: Writer?\n    ) -> LinuxProcess.Stdio {\n        var stdinSetup: LinuxProcess.StdioReaderSetup? = nil\n        if let reader = stdin {\n            let ret = portAllocator.wrappingAdd(1, ordering: .relaxed)\n            stdinSetup = .init(\n                port: ret.oldValue,\n                reader: reader\n            )\n        }\n\n        var stdoutSetup: LinuxProcess.StdioSetup? = nil\n        if let writer = stdout {\n            let ret = portAllocator.wrappingAdd(1, ordering: .relaxed)\n            stdoutSetup = LinuxProcess.StdioSetup(\n                port: ret.oldValue,\n                writer: writer\n            )\n        }\n\n        var stderrSetup: LinuxProcess.StdioSetup? = nil\n        if let writer = stderr {\n            let ret = portAllocator.wrappingAdd(1, ordering: .relaxed)\n            stderrSetup = LinuxProcess.StdioSetup(\n                port: ret.oldValue,\n                writer: writer\n            )\n        }\n\n        return LinuxProcess.Stdio(\n            stdin: stdinSetup,\n            stdout: stdoutSetup,\n            stderr: stderrSetup\n        )\n    }\n\n    /// Stop the container from executing. This MUST be called even if wait() has returned\n    /// as their are additional resources to free.\n    public func stop() async throws {\n        try await self.state.withLock { state in\n            // Allow stop to be called multiple times.\n            if case .stopped = state {\n                return\n            }\n\n            let vm: any VirtualMachineInstance\n            let relayManager: UnixSocketRelayManager\n            let fileMountContext: FileMountContext\n\n            let startedState = try? state.startedState(\"stop\")\n            if let startedState {\n                vm = startedState.vm\n                relayManager = startedState.relayManager\n                fileMountContext = startedState.fileMountContext\n            } else {\n                let createdState = try state.createdState(\"stop\")\n                vm = createdState.vm\n                relayManager = createdState.relayManager\n                fileMountContext = createdState.fileMountContext\n            }\n\n            var firstError: Error?\n            do {\n                try await relayManager.stopAll()\n            } catch {\n                self.logger?.error(\"failed to stop relay manager: \\(error)\")\n                firstError = firstError ?? error\n            }\n\n            do {\n                try await vm.withAgent { agent in\n                    // First, we need to stop any unix socket relays as this will\n                    // keep the rootfs from being able to umount (EBUSY).\n                    let sockets = self.config.sockets\n                    if !sockets.isEmpty {\n                        guard let relayAgent = agent as? SocketRelayAgent else {\n                            throw ContainerizationError(\n                                .unsupported,\n                                message: \"VirtualMachineAgent does not support relaySocket surface\"\n                            )\n                        }\n                        for socket in sockets {\n                            try await relayAgent.stopSocketRelay(configuration: socket)\n                        }\n                    }\n\n                    if let _ = startedState {\n                        // Now lets ensure every process is donezo.\n                        try await agent.kill(pid: -1, signal: SIGKILL)\n\n                        // Wait on init proc exit. Give it 5 seconds of leeway.\n                        _ = try await agent.waitProcess(\n                            id: self.id,\n                            containerID: self.id,\n                            timeoutInSeconds: 5\n                        )\n                    }\n\n                    // Today, we leave EBUSY looping and other fun logic up to the\n                    // guest agent.\n                    try await agent.umount(\n                        path: Self.guestRootfsPath(self.id),\n                        flags: 0\n                    )\n\n                    // If we have a writable layer, we also need to unmount the lower and upper layers.\n                    if self.writableLayer != nil {\n                        let upperPath = \"/run/container/\\(self.id)/upper\"\n                        let lowerPath = \"/run/container/\\(self.id)/lower\"\n                        try await agent.umount(path: upperPath, flags: 0)\n                        try await agent.umount(path: lowerPath, flags: 0)\n                    }\n\n                    try await agent.sync()\n                }\n            } catch {\n                self.logger?.error(\"failed during guest cleanup: \\(error)\")\n                firstError = firstError ?? error\n            }\n\n            if let startedState {\n                for process in startedState.vendedProcesses.values {\n                    do {\n                        try await process._delete()\n                    } catch {\n                        self.logger?.error(\"failed to delete process \\(process.id): \\(error)\")\n                        firstError = firstError ?? error\n                    }\n                }\n\n                do {\n                    try await startedState.process.delete()\n                } catch {\n                    self.logger?.error(\"failed to delete init process: \\(error)\")\n                    firstError = firstError ?? error\n                }\n            }\n\n            // Clean up file mount temporary directories.\n            fileMountContext.cleanUp()\n\n            do {\n                try await vm.stop()\n                state = .stopped\n                if let firstError {\n                    throw firstError\n                }\n            } catch {\n                self.logger?.error(\"failed to stop VM: \\(error)\")\n                let finalError = firstError ?? error\n                state.setErrored(error: finalError)\n                throw finalError\n            }\n        }\n    }\n\n    /// Send a signal to the container.\n    public func kill(_ signal: Int32) async throws {\n        try await self.state.withLock {\n            let state = try $0.startedState(\"kill\")\n            try await state.process.kill(signal)\n        }\n    }\n\n    /// Wait for the container to exit. Returns the exit code.\n    @discardableResult\n    public func wait(timeoutInSeconds: Int64? = nil) async throws -> ExitStatus {\n        let t = try await self.state.withLock {\n            let state = try $0.startedState(\"wait\")\n            let t = Task {\n                try await state.process.wait(timeoutInSeconds: timeoutInSeconds)\n            }\n            return t\n        }\n        return try await t.value\n    }\n\n    /// Resize the container's terminal (if one was requested). This\n    /// will error if terminal was set to false before creating the container.\n    public func resize(to: Terminal.Size) async throws {\n        try await self.state.withLock {\n            let state = try $0.startedState(\"resize\")\n            try await state.process.resize(to: to)\n        }\n    }\n\n    /// Execute a new process in the container. The process is not started after this call, and must be manually started\n    /// via the `start` method.\n    public func exec(_ id: String, configuration: @Sendable @escaping (inout LinuxProcessConfiguration) throws -> Void) async throws -> LinuxProcess {\n        try await self.state.withLock { state in\n            var startedState = try state.startedState(\"exec\")\n\n            var spec = self.generateRuntimeSpec()\n            var config = LinuxProcessConfiguration()\n            try configuration(&config)\n            spec.process = config.toOCI()\n\n            let stdio = IOUtil.setup(\n                portAllocator: self.hostVsockPorts,\n                stdin: config.stdin,\n                stdout: config.stdout,\n                stderr: config.stderr\n            )\n            let agent = try await startedState.vm.dialAgent()\n            let process = LinuxProcess(\n                id,\n                containerID: self.id,\n                spec: spec,\n                io: stdio,\n                ociRuntimePath: self.config.ociRuntimePath,\n                agent: agent,\n                vm: startedState.vm,\n                logger: self.logger,\n                onDelete: { [weak self] in\n                    await self?.removeProcess(id: id)\n                }\n            )\n\n            startedState.vendedProcesses[id] = process\n            state = .started(startedState)\n\n            return process\n        }\n    }\n\n    /// Execute a new process in the container. The process is not started after this call, and must be manually started\n    /// via the `start` method.\n    public func exec(_ id: String, configuration: LinuxProcessConfiguration) async throws -> LinuxProcess {\n        try await self.state.withLock {\n            var state = try $0.startedState(\"exec\")\n\n            var spec = self.generateRuntimeSpec()\n            spec.process = configuration.toOCI()\n\n            let stdio = IOUtil.setup(\n                portAllocator: self.hostVsockPorts,\n                stdin: configuration.stdin,\n                stdout: configuration.stdout,\n                stderr: configuration.stderr\n            )\n            let agent = try await state.vm.dialAgent()\n            let process = LinuxProcess(\n                id,\n                containerID: self.id,\n                spec: spec,\n                io: stdio,\n                ociRuntimePath: self.config.ociRuntimePath,\n                agent: agent,\n                vm: state.vm,\n                logger: self.logger,\n                onDelete: { [weak self] in\n                    await self?.removeProcess(id: id)\n                }\n            )\n\n            state.vendedProcesses[id] = process\n            $0 = .started(state)\n\n            return process\n        }\n    }\n\n    /// Dial a vsock port in the container.\n    public func dialVsock(port: UInt32) async throws -> FileHandle {\n        try await self.state.withLock {\n            let state = try $0.startedState(\"dialVsock\")\n            return try await state.vm.dial(port)\n        }\n    }\n\n    /// Close the containers standard input to signal no more input is\n    /// arriving.\n    public func closeStdin() async throws {\n        try await self.state.withLock {\n            let state = try $0.startedState(\"closeStdin\")\n            return try await state.process.closeStdin()\n        }\n    }\n\n    /// Remove a process from the vended processes tracking.\n    private func removeProcess(id: String) async {\n        await self.state.withLock {\n            guard case .started(var state) = $0 else {\n                return\n            }\n            state.vendedProcesses.removeValue(forKey: id)\n            $0 = .started(state)\n        }\n    }\n\n    /// Get statistics for the container.\n    public func statistics(categories: StatCategory = .all) async throws -> ContainerStatistics {\n        try await self.state.withLock {\n            let state = try $0.startedState(\"statistics\")\n\n            let stats = try await state.vm.withAgent { agent in\n                let allStats = try await agent.containerStatistics(containerIDs: [self.id], categories: categories)\n                guard let containerStats = allStats.first else {\n                    throw ContainerizationError(\n                        .notFound,\n                        message: \"statistics for container \\(self.id) not found\"\n                    )\n                }\n                return containerStats\n            }\n\n            return stats\n        }\n    }\n\n    private func relayUnixSocket(\n        socket: UnixSocketConfiguration,\n        relayManager: UnixSocketRelayManager,\n        agent: any VirtualMachineAgent\n    ) async throws {\n        guard let relayAgent = agent as? SocketRelayAgent else {\n            throw ContainerizationError(\n                .unsupported,\n                message: \"VirtualMachineAgent does not support relaySocket surface\"\n            )\n        }\n\n        var socket = socket\n        let rootInGuest = URL(filePath: self.root)\n\n        let port: UInt32\n        if socket.direction == .into {\n            port = self.hostVsockPorts.wrappingAdd(1, ordering: .relaxed).oldValue\n            socket.destination = URL(filePath: Self.guestSocketStagingPath(self.id, socketID: socket.id))\n        } else {\n            port = self.guestVsockPorts.wrappingAdd(1, ordering: .relaxed).oldValue\n            socket.source = rootInGuest.appending(path: socket.source.path)\n        }\n\n        try await relayManager.start(port: port, socket: socket)\n        try await relayAgent.relaySocket(port: port, configuration: socket)\n    }\n\n    /// Default chunk size for file transfers (1MiB).\n    public static let defaultCopyChunkSize = 1024 * 1024\n\n    /// Copy a file or directory from the host into the container.\n    ///\n    /// Data transfer happens over a dedicated vsock connection. For directories,\n    /// the source is archived as tar+gzip and streamed directly through vsock\n    /// without intermediate temp files.\n    public func copyIn(\n        from source: URL,\n        to destination: URL,\n        mode: UInt32 = 0o644,\n        createParents: Bool = true,\n        chunkSize: Int = defaultCopyChunkSize\n    ) async throws {\n        try await self.state.withLock {\n            let state = try $0.startedState(\"copyIn\")\n\n            var isDirectory: ObjCBool = false\n            guard FileManager.default.fileExists(atPath: source.path, isDirectory: &isDirectory) else {\n                throw ContainerizationError(.notFound, message: \"copyIn: source not found '\\(source.path)'\")\n            }\n            let isArchive = isDirectory.boolValue\n\n            let guestPath = URL(filePath: self.root).appending(path: destination.path)\n            let port = self.hostVsockPorts.wrappingAdd(1, ordering: .relaxed).oldValue\n            let listener = try state.vm.listen(port)\n\n            try await withThrowingTaskGroup(of: Void.self) { group in\n                group.addTask {\n                    try await state.vm.withAgent { agent in\n                        guard let vminitd = agent as? Vminitd else {\n                            throw ContainerizationError(.unsupported, message: \"copyIn requires Vminitd agent\")\n                        }\n                        try await vminitd.copy(\n                            direction: .copyIn,\n                            guestPath: guestPath,\n                            vsockPort: port,\n                            mode: mode,\n                            createParents: createParents,\n                            isArchive: isArchive\n                        )\n                    }\n                }\n\n                group.addTask {\n                    guard let conn = await listener.first(where: { _ in true }) else {\n                        throw ContainerizationError(.internalError, message: \"copyIn: vsock connection not established\")\n                    }\n                    try listener.finish()\n\n                    try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, any Error>) in\n                        self.copyQueue.async {\n                            do {\n                                defer { conn.closeFile() }\n\n                                if isArchive {\n                                    let writer = try ArchiveWriter(configuration: .init(format: .pax, filter: .gzip))\n                                    try writer.open(fileDescriptor: conn.fileDescriptor)\n                                    try writer.archiveDirectory(source)\n                                    try writer.finishEncoding()\n                                } else {\n                                    let srcFd = open(source.path, O_RDONLY)\n                                    guard srcFd != -1 else {\n                                        throw ContainerizationError(\n                                            .internalError,\n                                            message: \"copyIn: failed to open '\\(source.path)': \\(String(cString: strerror(errno)))\"\n                                        )\n                                    }\n                                    defer { close(srcFd) }\n\n                                    var buf = [UInt8](repeating: 0, count: chunkSize)\n                                    while true {\n                                        let n = read(srcFd, &buf, buf.count)\n                                        if n == 0 { break }\n                                        guard n > 0 else {\n                                            throw ContainerizationError(\n                                                .internalError,\n                                                message: \"copyIn: read error: \\(String(cString: strerror(errno)))\"\n                                            )\n                                        }\n                                        var written = 0\n                                        while written < n {\n                                            let w = buf.withUnsafeBytes { ptr in\n                                                write(conn.fileDescriptor, ptr.baseAddress! + written, n - written)\n                                            }\n                                            guard w > 0 else {\n                                                throw ContainerizationError(\n                                                    .internalError,\n                                                    message: \"copyIn: vsock write error: \\(String(cString: strerror(errno)))\"\n                                                )\n                                            }\n                                            written += w\n                                        }\n                                    }\n                                }\n                                continuation.resume()\n                            } catch {\n                                continuation.resume(throwing: error)\n                            }\n                        }\n                    }\n                }\n\n                try await group.waitForAll()\n            }\n        }\n    }\n\n    /// Copy a file or directory from the container to the host.\n    ///\n    /// Data transfer happens over a dedicated vsock connection. For directories,\n    /// the guest archives the source as tar+gzip and streams it directly through\n    /// vsock. The host extracts the archive without intermediate temp files.\n    public func copyOut(\n        from source: URL,\n        to destination: URL,\n        createParents: Bool = true,\n        chunkSize: Int = defaultCopyChunkSize\n    ) async throws {\n        try await self.state.withLock {\n            let state = try $0.startedState(\"copyOut\")\n\n            if createParents {\n                let parentDir = destination.deletingLastPathComponent()\n                try FileManager.default.createDirectory(at: parentDir, withIntermediateDirectories: true)\n            }\n\n            let guestPath = URL(filePath: self.root).appending(path: source.path)\n            let port = self.hostVsockPorts.wrappingAdd(1, ordering: .relaxed).oldValue\n            let listener = try state.vm.listen(port)\n\n            let (metadataStream, metadataCont) = AsyncStream.makeStream(of: Vminitd.CopyMetadata.self)\n\n            try await withThrowingTaskGroup(of: Void.self) { group in\n                group.addTask {\n                    try await state.vm.withAgent { agent in\n                        guard let vminitd = agent as? Vminitd else {\n                            throw ContainerizationError(.unsupported, message: \"copyOut requires Vminitd agent\")\n                        }\n                        try await vminitd.copy(\n                            direction: .copyOut,\n                            guestPath: guestPath,\n                            vsockPort: port,\n                            onMetadata: { meta in\n                                metadataCont.yield(meta)\n                                metadataCont.finish()\n                            }\n                        )\n                    }\n                }\n\n                group.addTask {\n                    guard let metadata = await metadataStream.first(where: { _ in true }) else {\n                        throw ContainerizationError(.internalError, message: \"copyOut: no metadata received\")\n                    }\n\n                    guard let conn = await listener.first(where: { _ in true }) else {\n                        throw ContainerizationError(.internalError, message: \"copyOut: vsock connection not established\")\n                    }\n                    try listener.finish()\n\n                    try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, any Error>) in\n                        self.copyQueue.async {\n                            do {\n                                defer { conn.closeFile() }\n\n                                if metadata.isArchive {\n                                    try FileManager.default.createDirectory(at: destination, withIntermediateDirectories: true)\n                                    let fh = FileHandle(fileDescriptor: dup(conn.fileDescriptor), closeOnDealloc: true)\n                                    let reader = try ArchiveReader(format: .pax, filter: .gzip, fileHandle: fh)\n                                    _ = try reader.extractContents(to: destination)\n                                } else {\n                                    let destFd = open(destination.path, O_WRONLY | O_CREAT | O_TRUNC, 0o644)\n                                    guard destFd != -1 else {\n                                        throw ContainerizationError(\n                                            .internalError,\n                                            message: \"copyOut: failed to open '\\(destination.path)': \\(String(cString: strerror(errno)))\"\n                                        )\n                                    }\n                                    defer { close(destFd) }\n\n                                    var buf = [UInt8](repeating: 0, count: chunkSize)\n                                    while true {\n                                        let n = read(conn.fileDescriptor, &buf, buf.count)\n                                        if n == 0 { break }\n                                        guard n > 0 else {\n                                            throw ContainerizationError(\n                                                .internalError,\n                                                message: \"copyOut: vsock read error: \\(String(cString: strerror(errno)))\"\n                                            )\n                                        }\n                                        var written = 0\n                                        while written < n {\n                                            let w = buf.withUnsafeBytes { ptr in\n                                                write(destFd, ptr.baseAddress! + written, n - written)\n                                            }\n                                            guard w > 0 else {\n                                                throw ContainerizationError(\n                                                    .internalError,\n                                                    message: \"copyOut: write error: \\(String(cString: strerror(errno)))\"\n                                                )\n                                            }\n                                            written += w\n                                        }\n                                    }\n                                }\n                                continuation.resume()\n                            } catch {\n                                continuation.resume(throwing: error)\n                            }\n                        }\n                    }\n                }\n\n                try await group.waitForAll()\n            }\n        }\n    }\n}\n\nextension VirtualMachineInstance {\n    /// Scoped access to an agent instance to ensure the resources are always freed (mostly close(2)'ing\n    /// the vsock fd)\n    func withAgent<T>(fn: @Sendable (VirtualMachineAgent) async throws -> T) async throws -> T {\n        let agent = try await self.dialAgent()\n        do {\n            let result = try await fn(agent)\n            try await agent.close()\n            return result\n        } catch {\n            try? await agent.close()\n            throw error\n        }\n    }\n}\n\nextension AttachedFilesystem {\n    var to: ContainerizationOCI.Mount {\n        .init(\n            type: self.type,\n            source: self.source,\n            destination: self.destination,\n            options: self.options\n        )\n    }\n}\n\nstruct IOUtil {\n    static func setup(\n        portAllocator: borrowing Atomic<UInt32>,\n        stdin: ReaderStream?,\n        stdout: Writer?,\n        stderr: Writer?\n    ) -> LinuxProcess.Stdio {\n        var stdinSetup: LinuxProcess.StdioReaderSetup? = nil\n        if let reader = stdin {\n            let ret = portAllocator.wrappingAdd(1, ordering: .relaxed)\n            stdinSetup = .init(\n                port: ret.oldValue,\n                reader: reader\n            )\n        }\n\n        var stdoutSetup: LinuxProcess.StdioSetup? = nil\n        if let writer = stdout {\n            let ret = portAllocator.wrappingAdd(1, ordering: .relaxed)\n            stdoutSetup = LinuxProcess.StdioSetup(\n                port: ret.oldValue,\n                writer: writer\n            )\n        }\n\n        var stderrSetup: LinuxProcess.StdioSetup? = nil\n        if let writer = stderr {\n            let ret = portAllocator.wrappingAdd(1, ordering: .relaxed)\n            stderrSetup = LinuxProcess.StdioSetup(\n                port: ret.oldValue,\n                writer: writer\n            )\n        }\n\n        return LinuxProcess.Stdio(\n            stdin: stdinSetup,\n            stdout: stdoutSetup,\n            stderr: stderrSetup\n        )\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Containerization/LinuxPod.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(macOS)\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport Foundation\nimport Logging\nimport Synchronization\n\nimport struct ContainerizationOS.Terminal\n\n/// NOTE: Experimental API\n///\n/// `LinuxPod` allows managing multiple Linux containers within a single\n/// virtual machine. Each container has its own rootfs and process, but\n/// shares the VM's resources (CPU, memory, network).\npublic final class LinuxPod: Sendable {\n    /// The identifier of the pod.\n    public let id: String\n\n    /// Configuration for the pod.\n    public let config: Configuration\n\n    /// The configuration for the LinuxPod.\n    public struct Configuration: Sendable {\n        /// The amount of cpus for the pod's VM.\n        public var cpus: Int = 4\n        /// The memory in bytes to give to the pod's VM.\n        public var memoryInBytes: UInt64 = 1024.mib()\n        /// The network interfaces for the pod.\n        public var interfaces: [any Interface] = []\n        /// Whether nested virtualization should be turned on for the pod.\n        public var virtualization: Bool = false\n        /// Optional file path to store serial boot logs.\n        public var bootLog: BootLog?\n        /// Whether containers in the pod should share a PID namespace.\n        /// When enabled, all containers can see each other's processes.\n        public var shareProcessNamespace: Bool = false\n        /// The default hostname for all containers in the pod.\n        /// Individual containers can override this by setting their own `hostname` configuration.\n        public var hostname: String?\n        /// The default DNS configuration for all containers in the pod.\n        /// Individual containers can override this by setting their own `dns` configuration.\n        public var dns: DNS?\n        /// The default hosts file configuration for all containers in the pod.\n        /// Individual containers can override this by setting their own `hosts` configuration.\n        public var hosts: Hosts?\n\n        public init() {}\n    }\n\n    /// Configuration for a container within the pod.\n    public struct ContainerConfiguration: Sendable {\n        /// Configuration for the init process of the container.\n        public var process = LinuxProcessConfiguration()\n        /// Optional per-container CPU limit (can exceed pod total for oversubscription).\n        public var cpus: Int?\n        /// Optional per-container memory limit in bytes (can exceed pod total for oversubscription).\n        public var memoryInBytes: UInt64?\n        /// The hostname for the container.\n        public var hostname: String?\n        /// The system control options for the container.\n        public var sysctl: [String: String] = [:]\n        /// The mounts for the container.\n        public var mounts: [Mount] = LinuxContainer.defaultMounts()\n        /// The Unix domain socket relays to setup for the container.\n        public var sockets: [UnixSocketConfiguration] = []\n        /// The DNS configuration for the container.\n        public var dns: DNS?\n        /// The hosts file configuration for the container.\n        public var hosts: Hosts?\n        /// Run the container with a minimal init process that handles signal\n        /// forwarding and zombie reaping.\n        public var useInit: Bool = false\n\n        public init() {}\n    }\n\n    private struct PodContainer: Sendable {\n        let id: String\n        let rootfs: Mount\n        let config: ContainerConfiguration\n        var state: ContainerState\n        var process: LinuxProcess?\n        var fileMountContext: FileMountContext\n\n        enum ContainerState: Sendable {\n            case registered\n            case created\n            case started\n            case stopped\n            case errored\n        }\n    }\n\n    private let state: AsyncMutex<State>\n\n    // Ports to be allocated from for stdio and for\n    // unix socket relays that are sharing a guest\n    // uds to the host.\n    private let hostVsockPorts: Atomic<UInt32>\n    // Ports we request the guest to allocate for unix socket relays from\n    // the host.\n    private let guestVsockPorts: Atomic<UInt32>\n\n    private struct State: Sendable {\n        var phase: Phase\n        var containers: [String: PodContainer]\n        var pauseProcess: LinuxProcess?\n    }\n\n    private enum Phase: Sendable {\n        /// The pod has been created but no live resources are running.\n        case initialized\n        /// The pod's virtual machine has been setup and the runtime environment has been configured.\n        case created(CreatedState)\n        /// An error occurred during the lifetime of this class.\n        case errored(Swift.Error)\n\n        struct CreatedState: Sendable {\n            let vm: any VirtualMachineInstance\n            let relayManager: UnixSocketRelayManager\n        }\n\n        func createdState(_ operation: String) throws -> CreatedState {\n            switch self {\n            case .created(let state):\n                return state\n            case .errored(let err):\n                throw err\n            default:\n                throw ContainerizationError(\n                    .invalidState,\n                    message: \"failed to \\(operation): pod must be created\"\n                )\n            }\n        }\n\n        mutating func validateForCreate() throws {\n            switch self {\n            case .initialized:\n                break\n            case .errored(let err):\n                throw err\n            default:\n                throw ContainerizationError(\n                    .invalidState,\n                    message: \"pod must be in initialized state to create\"\n                )\n            }\n        }\n\n        mutating func setErrored(error: Swift.Error) {\n            self = .errored(error)\n        }\n    }\n\n    private let vmm: VirtualMachineManager\n    private let logger: Logger?\n\n    /// Create a new `LinuxPod`. A `VirtualMachineManager` instance must be\n    /// provided that will handle launching the virtual machine the containers\n    /// will execute inside of.\n    public init(\n        _ id: String,\n        vmm: VirtualMachineManager,\n        logger: Logger? = nil,\n        configuration: (inout Configuration) throws -> Void\n    ) throws {\n        self.id = id\n        self.vmm = vmm\n        self.hostVsockPorts = Atomic<UInt32>(0x1000_0000)\n        self.guestVsockPorts = Atomic<UInt32>(0x1000_0000)\n        self.logger = logger\n\n        var config = Configuration()\n        try configuration(&config)\n\n        self.config = config\n        self.state = AsyncMutex(State(phase: .initialized, containers: [:], pauseProcess: nil))\n    }\n\n    private static func createDefaultRuntimeSpec(_ containerID: String, podID: String) -> Spec {\n        .init(\n            process: .init(),\n            hostname: containerID,\n            root: .init(\n                path: Self.guestRootfsPath(containerID),\n                readonly: false\n            ),\n            linux: .init(\n                resources: .init(),\n                cgroupsPath: \"/container/pod/\\(podID)/\\(containerID)\"\n            )\n        )\n    }\n\n    private func generateRuntimeSpec(containerID: String, config: ContainerConfiguration, rootfs: Mount) -> Spec {\n        var spec = Self.createDefaultRuntimeSpec(containerID, podID: self.id)\n\n        // Process configuration\n        spec.process = config.process.toOCI()\n\n        // Wrap with init process if requested.\n        if config.useInit {\n            let originalArgs = spec.process?.args ?? []\n            spec.process?.args = [\"/.cz-init\", \"--\"] + originalArgs\n        }\n\n        // General toggles\n        // Container-level hostname takes precedence; fall back to pod-level hostname.\n        if let hostname = config.hostname ?? self.config.hostname {\n            spec.hostname = hostname\n        }\n\n        // Linux toggles\n        spec.linux?.sysctl = config.sysctl\n\n        // If the rootfs was requested as read-only, set it in the OCI spec.\n        // We let the OCI runtime remount as ro, instead of doing it originally.\n        spec.root?.readonly = rootfs.options.contains(\"ro\")\n\n        // Resource limits (if specified)\n        if let cpus = config.cpus, cpus > 0 {\n            spec.linux?.resources?.cpu = LinuxCPU(\n                quota: Int64(cpus * 100_000),\n                period: 100_000\n            )\n        }\n        if let memoryInBytes = config.memoryInBytes, memoryInBytes > 0 {\n            spec.linux?.resources?.memory = LinuxMemory(\n                limit: Int64(memoryInBytes)\n            )\n        }\n\n        return spec\n    }\n\n    private static func guestRootfsPath(_ containerID: String) -> String {\n        \"/run/container/\\(containerID)/rootfs\"\n    }\n\n    private static func guestSocketStagingPath(_ containerID: String, socketID: String) -> String {\n        \"/run/container/\\(containerID)/sockets/\\(socketID).sock\"\n    }\n}\n\nextension LinuxPod {\n    /// Number of CPU cores allocated to the pod's VM.\n    public var cpus: Int {\n        config.cpus\n    }\n\n    /// Amount of memory in bytes allocated for the pod's VM.\n    public var memoryInBytes: UInt64 {\n        config.memoryInBytes\n    }\n\n    /// Network interfaces of the pod.\n    public var interfaces: [any Interface] {\n        config.interfaces\n    }\n\n    /// Add a container to the pod. This must be called before `create()`.\n    /// The container will be registered but not started.\n    public func addContainer(\n        _ id: String,\n        rootfs: Mount,\n        configuration: @Sendable @escaping (inout ContainerConfiguration) throws -> Void\n    ) async throws {\n        try await self.state.withLock { state in\n            guard case .initialized = state.phase else {\n                throw ContainerizationError(\n                    .invalidState,\n                    message: \"pod must be initialized to add container\"\n                )\n            }\n\n            guard state.containers[id] == nil else {\n                throw ContainerizationError(\n                    .invalidArgument,\n                    message: \"container with id \\(id) already exists in pod\"\n                )\n            }\n\n            var config = ContainerConfiguration()\n            try configuration(&config)\n\n            // Prepare file mounts - transforms single-file mounts into directory shares.\n            let fileMountContext = try FileMountContext.prepare(mounts: config.mounts)\n\n            state.containers[id] = PodContainer(\n                id: id,\n                rootfs: rootfs,\n                config: config,\n                state: .registered,\n                process: nil,\n                fileMountContext: fileMountContext\n            )\n        }\n    }\n\n    /// Create and start the underlying pod's virtual machine and set up\n    /// the runtime environment. All registered containers will have their\n    /// rootfs mounted, but no init processes will be running.\n    public func create() async throws {\n        try await self.state.withLock { state in\n            try state.phase.validateForCreate()\n\n            // Build mountsByID for all containers.\n            // Strip \"ro\" from rootfs options - we handle readonly via the OCI spec's\n            // root.readonly field and remount in vmexec after setup is complete.\n            // Use transformedMounts from fileMountContext (file mounts become directory shares).\n            var mountsByID: [String: [Mount]] = [:]\n            for (id, container) in state.containers {\n                var modifiedRootfs = container.rootfs\n                modifiedRootfs.options.removeAll(where: { $0 == \"ro\" })\n                mountsByID[id] = [modifiedRootfs] + container.fileMountContext.transformedMounts\n            }\n\n            let vmConfig = VMConfiguration(\n                cpus: self.config.cpus,\n                memoryInBytes: self.config.memoryInBytes,\n                interfaces: self.config.interfaces,\n                mountsByID: mountsByID,\n                bootLog: self.config.bootLog,\n                nestedVirtualization: self.config.virtualization\n            )\n            let creationConfig = StandardVMConfig(configuration: vmConfig)\n            let vm = try await self.vmm.create(config: creationConfig)\n            let relayManager = UnixSocketRelayManager(vm: vm)\n            try await vm.start()\n\n            do {\n                let containers = state.containers\n                let shareProcessNamespace = self.config.shareProcessNamespace\n                let pauseProcessHolder = Mutex<LinuxProcess?>(nil)\n                let fileMountContextUpdates = Mutex<[String: FileMountContext]>([:])\n\n                try await vm.withAgent { agent in\n                    try await agent.standardSetup()\n\n                    // Create pause container if PID namespace sharing is enabled\n                    if shareProcessNamespace {\n                        let pauseID = \"pause-\\(self.id)\"\n                        let pauseRootfsPath = \"/run/container/\\(pauseID)/rootfs\"\n\n                        // Bind mount /sbin into the pause container rootfs.\n                        // This is where the guest agent lives.\n                        try await agent.mount(\n                            ContainerizationOCI.Mount(\n                                type: \"\",\n                                source: \"/sbin\",\n                                destination: \"\\(pauseRootfsPath)/sbin\",\n                                options: [\"bind\"]\n                            ))\n\n                        var pauseSpec = Self.createDefaultRuntimeSpec(pauseID, podID: self.id)\n                        pauseSpec.process?.args = [\"/sbin/vminitd\", \"pause\"]\n                        pauseSpec.hostname = \"\"\n                        pauseSpec.mounts = LinuxContainer.defaultMounts().map {\n                            ContainerizationOCI.Mount(\n                                type: $0.type,\n                                source: $0.source,\n                                destination: $0.destination,\n                                options: $0.options\n                            )\n                        }\n                        pauseSpec.linux?.namespaces = [\n                            LinuxNamespace(type: .cgroup),\n                            LinuxNamespace(type: .ipc),\n                            LinuxNamespace(type: .mount),\n                            LinuxNamespace(type: .pid),\n                            LinuxNamespace(type: .uts),\n                        ]\n\n                        // Create LinuxProcess for pause container\n                        let process = LinuxProcess(\n                            pauseID,\n                            containerID: pauseID,\n                            spec: pauseSpec,\n                            io: LinuxProcess.Stdio(stdin: nil, stdout: nil, stderr: nil),\n                            ociRuntimePath: nil,\n                            agent: agent,\n                            vm: vm,\n                            logger: self.logger\n                        )\n\n                        try await process.start()\n                        pauseProcessHolder.withLock { $0 = process }\n\n                        self.logger?.debug(\"Pause container started\", metadata: [\"pid\": \"\\(process.pid)\"])\n                    }\n\n                    // Mount all container rootfs\n                    for (_, container) in containers {\n                        guard let attachments = vm.mounts[container.id], let rootfsAttachment = attachments.first else {\n                            throw ContainerizationError(.notFound, message: \"rootfs mount not found for container \\(container.id)\")\n                        }\n                        var rootfs = rootfsAttachment.to\n                        rootfs.destination = Self.guestRootfsPath(container.id)\n                        try await agent.mount(rootfs)\n                    }\n\n                    // Mount file mount holding directories under /run for each container.\n                    for (id, container) in containers {\n                        if container.fileMountContext.hasFileMounts {\n                            var ctx = container.fileMountContext\n                            let containerMounts = vm.mounts[id] ?? []\n                            try await ctx.mountHoldingDirectories(\n                                vmMounts: containerMounts,\n                                agent: agent\n                            )\n                            fileMountContextUpdates.withLock { $0[id] = ctx }\n                        }\n                    }\n\n                    // Start up unix socket relays for each container\n                    for (_, container) in containers {\n                        for socket in container.config.sockets {\n                            try await self.relayUnixSocket(\n                                socket: socket,\n                                containerID: container.id,\n                                relayManager: relayManager,\n                                agent: agent\n                            )\n                        }\n                    }\n\n                    // For every interface asked for:\n                    // 1. Add the address requested\n                    // 2. Online the adapter\n                    // 3. If a gateway IP address is present, add the default route.\n                    for (index, i) in self.interfaces.enumerated() {\n                        let name = \"eth\\(index)\"\n                        self.logger?.debug(\"setting up interface \\(name) with address \\(i.ipv4Address)\")\n                        try await agent.addressAdd(name: name, ipv4Address: i.ipv4Address)\n                        try await agent.up(name: name, mtu: i.mtu)\n                        if let ipv4Gateway = i.ipv4Gateway {\n                            if !i.ipv4Address.contains(ipv4Gateway) {\n                                self.logger?.debug(\"gateway \\(ipv4Gateway) is outside subnet \\(i.ipv4Address), adding a route first\")\n                                try await agent.routeAddLink(name: name, dstIPv4Addr: ipv4Gateway, srcIPv4Addr: nil)\n                            }\n                            try await agent.routeAddDefault(name: name, ipv4Gateway: ipv4Gateway)\n                        }\n                    }\n\n                    // Setup /etc/resolv.conf and /etc/hosts for each container.\n                    // Container-level config takes precedence over pod-level config.\n                    for (_, container) in containers {\n                        if let dns = container.config.dns ?? self.config.dns {\n                            try await agent.configureDNS(\n                                config: dns,\n                                location: Self.guestRootfsPath(container.id)\n                            )\n                        }\n                        if let hosts = container.config.hosts ?? self.config.hosts {\n                            try await agent.configureHosts(\n                                config: hosts,\n                                location: Self.guestRootfsPath(container.id)\n                            )\n                        }\n                    }\n                }\n\n                state.pauseProcess = pauseProcessHolder.withLock { $0 }\n\n                // Apply file mount context updates.\n                let updates = fileMountContextUpdates.withLock { $0 }\n                for (id, ctx) in updates {\n                    state.containers[id]?.fileMountContext = ctx\n                }\n\n                // Transition all containers to created state\n                for id in state.containers.keys {\n                    state.containers[id]?.state = .created\n                }\n\n                state.phase = .created(.init(vm: vm, relayManager: relayManager))\n            } catch {\n                try? await relayManager.stopAll()\n                try? await vm.stop()\n                state.phase.setErrored(error: error)\n                throw error\n            }\n        }\n    }\n\n    /// Start a container's initial process.\n    public func startContainer(_ containerID: String) async throws {\n        try await self.state.withLock { state in\n            let createdState = try state.phase.createdState(\"startContainer\")\n\n            guard var container = state.containers[containerID] else {\n                throw ContainerizationError(\n                    .notFound,\n                    message: \"container \\(containerID) not found in pod\"\n                )\n            }\n\n            guard container.state == .created else {\n                throw ContainerizationError(\n                    .invalidState,\n                    message: \"container \\(containerID) must be in created state to start\"\n                )\n            }\n\n            let agent = try await createdState.vm.dialAgent()\n            do {\n                var spec = self.generateRuntimeSpec(containerID: containerID, config: container.config, rootfs: container.rootfs)\n                // We don't need the rootfs, nor do OCI runtimes want it included.\n                // Also filter out file mount holding directories - we mount those separately under /run.\n                let containerMounts = createdState.vm.mounts[containerID] ?? []\n                let holdingTags = container.fileMountContext.holdingDirectoryTags\n                var mounts: [ContainerizationOCI.Mount] =\n                    containerMounts.dropFirst()\n                    .filter { !holdingTags.contains($0.source) }\n                    .map { $0.to }\n                    + container.fileMountContext.ociBindMounts()\n\n                // When useInit is enabled, bind mount vminitd from the VM's filesystem\n                // into the container so it can be executed.\n                if container.config.useInit {\n                    mounts.append(\n                        ContainerizationOCI.Mount(\n                            type: \"bind\",\n                            source: \"/sbin/vminitd\",\n                            destination: \"/.cz-init\",\n                            options: [\"bind\", \"ro\"]\n                        ))\n                }\n\n                // Bind mount staged sockets into the container. Sockets relayed\n                // .into the container are created in a staging directory outside\n                // the rootfs to avoid symlink traversal and mount shadowing.\n                for socket in container.config.sockets where socket.direction == .into {\n                    mounts.append(\n                        ContainerizationOCI.Mount(\n                            type: \"bind\",\n                            source: Self.guestSocketStagingPath(containerID, socketID: socket.id),\n                            destination: socket.destination.path,\n                            options: [\"bind\"]\n                        ))\n                }\n\n                spec.mounts = mounts\n\n                // Configure namespaces for the container\n                var namespaces: [LinuxNamespace] = [\n                    LinuxNamespace(type: .cgroup),\n                    LinuxNamespace(type: .ipc),\n                    LinuxNamespace(type: .mount),\n                    LinuxNamespace(type: .uts),\n                ]\n\n                // Either join pause container's pid ns or create a new one\n                if self.config.shareProcessNamespace, let pausePID = state.pauseProcess?.pid {\n                    let nsPath = \"/proc/\\(pausePID)/ns/pid\"\n\n                    self.logger?.debug(\n                        \"Container joining pause PID namespace\",\n                        metadata: [\n                            \"container\": \"\\(containerID)\",\n                            \"pausePID\": \"\\(pausePID)\",\n                            \"nsPath\": \"\\(nsPath)\",\n                        ])\n\n                    namespaces.append(LinuxNamespace(type: .pid, path: nsPath))\n                } else {\n                    namespaces.append(LinuxNamespace(type: .pid))\n                }\n\n                spec.linux?.namespaces = namespaces\n\n                let stdio = IOUtil.setup(\n                    portAllocator: self.hostVsockPorts,\n                    stdin: container.config.process.stdin,\n                    stdout: container.config.process.stdout,\n                    stderr: container.config.process.stderr\n                )\n\n                let process = LinuxProcess(\n                    containerID,\n                    containerID: containerID,\n                    spec: spec,\n                    io: stdio,\n                    ociRuntimePath: nil,\n                    agent: agent,\n                    vm: createdState.vm,\n                    logger: self.logger\n                )\n                try await process.start()\n\n                container.process = process\n                container.state = .started\n                state.containers[containerID] = container\n            } catch {\n                try? await agent.close()\n                throw error\n            }\n        }\n    }\n\n    /// Stop a container from executing.\n    public func stopContainer(_ containerID: String) async throws {\n        try await self.state.withLock { state in\n            let createdState = try state.phase.createdState(\"stopContainer\")\n\n            guard var container = state.containers[containerID] else {\n                throw ContainerizationError(\n                    .notFound,\n                    message: \"container \\(containerID) not found in pod\"\n                )\n            }\n\n            // Allow stop to be called multiple times\n            if container.state == .stopped {\n                return\n            }\n\n            guard container.state == .started, let process = container.process else {\n                throw ContainerizationError(\n                    .invalidState,\n                    message: \"container \\(containerID) must be in started state to stop\"\n                )\n            }\n\n            do {\n                // Check if the vm is even still running\n                if createdState.vm.state == .stopped {\n                    container.state = .stopped\n                    state.containers[containerID] = container\n                    return\n                }\n\n                try await process.kill(SIGKILL)\n                try await process.wait(timeoutInSeconds: 3)\n\n                try await createdState.vm.withAgent { agent in\n                    // Unmount the rootfs\n                    try await agent.umount(\n                        path: Self.guestRootfsPath(containerID),\n                        flags: 0\n                    )\n                }\n\n                // Clean up the process resources\n                try await process.delete()\n\n                container.process = nil\n                container.state = .stopped\n                state.containers[containerID] = container\n            } catch {\n                container.state = .errored\n                container.process = nil\n                state.containers[containerID] = container\n\n                throw error\n            }\n        }\n    }\n\n    /// Stop the pod's VM and all containers.\n    public func stop() async throws {\n        try await self.state.withLock { state in\n            let createdState = try state.phase.createdState(\"stop\")\n\n            do {\n                try await createdState.relayManager.stopAll()\n\n                // Stop all containers\n                let containerIDs = Array(state.containers.keys)\n\n                for containerID in containerIDs {\n                    // Stop the container inline\n                    guard var container = state.containers[containerID] else {\n                        continue\n                    }\n\n                    if container.state == .stopped {\n                        continue\n                    }\n\n                    if let process = container.process, container.state == .started {\n                        if createdState.vm.state != .stopped {\n                            try? await process.kill(SIGKILL)\n                            _ = try? await process.wait(timeoutInSeconds: 3)\n\n                            try? await createdState.vm.withAgent { agent in\n                                try await agent.umount(\n                                    path: Self.guestRootfsPath(containerID),\n                                    flags: 0\n                                )\n                            }\n                        }\n\n                        try? await process.delete()\n                        container.process = nil\n                        container.state = .stopped\n\n                        // Clean up file mount temporary directories.\n                        container.fileMountContext.cleanUp()\n\n                        state.containers[containerID] = container\n                    }\n                }\n\n                try await createdState.vm.stop()\n                state.phase = .initialized\n            } catch {\n                try? await createdState.vm.stop()\n                state.phase.setErrored(error: error)\n                throw error\n            }\n        }\n    }\n\n    /// Send a signal to a container.\n    public func killContainer(_ containerID: String, signal: Int32) async throws {\n        try await self.state.withLock { state in\n            guard let container = state.containers[containerID], let process = container.process else {\n                throw ContainerizationError(\n                    .notFound,\n                    message: \"container \\(containerID) not found or not started\"\n                )\n            }\n            try await process.kill(signal)\n        }\n    }\n\n    /// Wait for a container to exit. Returns the exit code.\n    @discardableResult\n    public func waitContainer(_ containerID: String, timeoutInSeconds: Int64? = nil) async throws -> ExitStatus {\n        let process = try await self.state.withLock { state in\n            guard let container = state.containers[containerID], let process = container.process else {\n                throw ContainerizationError(\n                    .notFound,\n                    message: \"container \\(containerID) not found or not started\"\n                )\n            }\n            return process\n        }\n        return try await process.wait(timeoutInSeconds: timeoutInSeconds)\n    }\n\n    /// Resize a container's terminal (if one was requested).\n    public func resizeContainer(_ containerID: String, to: Terminal.Size) async throws {\n        try await self.state.withLock { state in\n            guard let container = state.containers[containerID], let process = container.process else {\n                throw ContainerizationError(\n                    .notFound,\n                    message: \"container \\(containerID) not found or not started\"\n                )\n            }\n            try await process.resize(to: to)\n        }\n    }\n\n    /// Execute a new process in a container.\n    public func execInContainer(\n        _ containerID: String,\n        processID: String,\n        configuration: @Sendable @escaping (inout LinuxProcessConfiguration) throws -> Void\n    ) async throws -> LinuxProcess {\n        try await self.state.withLock { state in\n            let createdState = try state.phase.createdState(\"execInContainer\")\n\n            guard let container = state.containers[containerID] else {\n                throw ContainerizationError(\n                    .notFound,\n                    message: \"container \\(containerID) not found in pod\"\n                )\n            }\n\n            guard container.state == .started else {\n                throw ContainerizationError(\n                    .invalidState,\n                    message: \"container \\(containerID) must be started to exec\"\n                )\n            }\n\n            var spec = self.generateRuntimeSpec(containerID: containerID, config: container.config, rootfs: container.rootfs)\n            // Inherit environment variables, working directory, user, capabilities, rlimits from container process.\n            // Reset: process arguments, terminal, stdio as these are not supposed to be inherited.\n            var config = container.config.process\n            config.arguments = []\n            config.terminal = false\n            config.stdin = nil\n            config.stdout = nil\n            config.stderr = nil\n            try configuration(&config)\n            spec.process = config.toOCI()\n\n            let stdio = IOUtil.setup(\n                portAllocator: self.hostVsockPorts,\n                stdin: config.stdin,\n                stdout: config.stdout,\n                stderr: config.stderr\n            )\n            let agent = try await createdState.vm.dialAgent()\n            let process = LinuxProcess(\n                processID,\n                containerID: containerID,\n                spec: spec,\n                io: stdio,\n                ociRuntimePath: nil,\n                agent: agent,\n                vm: createdState.vm,\n                logger: self.logger\n            )\n            return process\n        }\n    }\n\n    /// List all container IDs in the pod.\n    public func listContainers() async -> [String] {\n        await self.state.withLock { state in\n            Array(state.containers.keys)\n        }\n    }\n\n    /// Get statistics for containers in the pod.\n    public func statistics(containerIDs: [String]? = nil, categories: StatCategory = .all) async throws -> [ContainerStatistics] {\n        let (createdState, ids) = try await self.state.withLock { state in\n            let createdState = try state.phase.createdState(\"statistics\")\n            let ids = containerIDs ?? Array(state.containers.keys)\n            return (createdState, ids)\n        }\n\n        let stats = try await createdState.vm.withAgent { agent in\n            try await agent.containerStatistics(containerIDs: ids, categories: categories)\n        }\n\n        return stats\n    }\n\n    /// Dial a vsock port in the pod's VM.\n    public func dialVsock(port: UInt32) async throws -> FileHandle {\n        try await self.state.withLock { state in\n            let createdState = try state.phase.createdState(\"dialVsock\")\n            return try await createdState.vm.dial(port)\n        }\n    }\n\n    /// Close a container's standard input to signal no more input is arriving.\n    public func closeContainerStdin(_ containerID: String) async throws {\n        try await self.state.withLock { state in\n            guard let container = state.containers[containerID], let process = container.process else {\n                throw ContainerizationError(\n                    .notFound,\n                    message: \"container \\(containerID) not found or not started\"\n                )\n            }\n            try await process.closeStdin()\n        }\n    }\n\n    /// Relay a unix socket for a container.\n    public func relayUnixSocket(_ containerID: String, socket: UnixSocketConfiguration) async throws {\n        try await self.state.withLock { state in\n            let createdState = try state.phase.createdState(\"relayUnixSocket\")\n\n            guard let _ = state.containers[containerID] else {\n                throw ContainerizationError(\n                    .notFound,\n                    message: \"container \\(containerID) not found in pod\"\n                )\n            }\n\n            try await createdState.vm.withAgent { agent in\n                try await self.relayUnixSocket(\n                    socket: socket,\n                    containerID: containerID,\n                    relayManager: createdState.relayManager,\n                    agent: agent\n                )\n            }\n        }\n    }\n\n    private func relayUnixSocket(\n        socket: UnixSocketConfiguration,\n        containerID: String,\n        relayManager: UnixSocketRelayManager,\n        agent: any VirtualMachineAgent\n    ) async throws {\n        guard let relayAgent = agent as? SocketRelayAgent else {\n            throw ContainerizationError(\n                .unsupported,\n                message: \"VirtualMachineAgent does not support relaySocket surface\"\n            )\n        }\n\n        var socket = socket\n\n        // Adjust paths to be relative to the container's rootfs\n        let rootInGuest = URL(filePath: Self.guestRootfsPath(containerID))\n\n        let port: UInt32\n        if socket.direction == .into {\n            port = self.hostVsockPorts.wrappingAdd(1, ordering: .relaxed).oldValue\n            socket.destination = URL(filePath: Self.guestSocketStagingPath(containerID, socketID: socket.id))\n        } else {\n            port = self.guestVsockPorts.wrappingAdd(1, ordering: .relaxed).oldValue\n            socket.source = rootInGuest.appending(path: socket.source.path)\n        }\n\n        try await relayManager.start(port: port, socket: socket)\n        try await relayAgent.relaySocket(port: port, configuration: socket)\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Containerization/LinuxProcess.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport ContainerizationOS\nimport Foundation\nimport Logging\nimport Synchronization\n\n/// `LinuxProcess` represents a Linux process and is used to\n/// setup and control the full lifecycle for the process.\npublic final class LinuxProcess: Sendable {\n    /// The ID of the process. This is purely metadata for the caller.\n    public let id: String\n\n    /// What container owns this process (if any).\n    public let owningContainer: String?\n\n    package struct StdioSetup: Sendable {\n        let port: UInt32\n        let writer: Writer\n    }\n\n    package struct StdioReaderSetup {\n        let port: UInt32\n        let reader: ReaderStream\n    }\n\n    package struct Stdio: Sendable {\n        let stdin: StdioReaderSetup?\n        let stdout: StdioSetup?\n        let stderr: StdioSetup?\n    }\n\n    private struct StdioHandles: Sendable {\n        var stdin: FileHandle?\n        var stdout: FileHandle?\n        var stderr: FileHandle?\n\n        mutating func close() throws {\n            if let stdin {\n                try stdin.close()\n                stdin.readabilityHandler = nil\n                self.stdin = nil\n            }\n            if let stdout {\n                try stdout.close()\n                stdout.readabilityHandler = nil\n                self.stdout = nil\n            }\n            if let stderr {\n                try stderr.close()\n                stderr.readabilityHandler = nil\n                self.stderr = nil\n            }\n        }\n    }\n\n    private struct State {\n        var spec: ContainerizationOCI.Spec\n        var pid: Int32\n        var stdio: StdioHandles\n        var stdinRelay: Task<(), Never>?\n        var ioTracker: IoTracker?\n        var deletionTask: Task<Void, Error>?\n\n        struct IoTracker {\n            let stream: AsyncStream<Void>\n            let cont: AsyncStream<Void>.Continuation\n            let configuredStreams: Int\n        }\n    }\n\n    /// The process ID for the container process. This will be -1\n    /// if the process has not been started.\n    public var pid: Int32 {\n        state.withLock { $0.pid }\n    }\n\n    private let state: Mutex<State>\n    private let ioSetup: Stdio\n    private let agent: any VirtualMachineAgent\n    private let vm: any VirtualMachineInstance\n    private let ociRuntimePath: String?\n    private let logger: Logger?\n    private let onDelete: (@Sendable () async -> Void)?\n\n    init(\n        _ id: String,\n        containerID: String? = nil,\n        spec: Spec,\n        io: Stdio,\n        ociRuntimePath: String?,\n        agent: any VirtualMachineAgent,\n        vm: any VirtualMachineInstance,\n        logger: Logger?,\n        onDelete: (@Sendable () async -> Void)? = nil\n    ) {\n        self.id = id\n        self.owningContainer = containerID\n        self.state = Mutex<State>(.init(spec: spec, pid: -1, stdio: StdioHandles()))\n        self.ioSetup = io\n        self.agent = agent\n        self.ociRuntimePath = ociRuntimePath\n        self.vm = vm\n        self.logger = logger\n        self.onDelete = onDelete\n    }\n}\n\nextension LinuxProcess {\n    func setupIO(listeners: [VsockListener?]) async throws -> [FileHandle?] {\n        let handles = try await Timeout.run(seconds: 3) {\n            try await withThrowingTaskGroup(of: (Int, FileHandle?).self) { group in\n                var results = [FileHandle?](repeating: nil, count: 3)\n\n                for (index, listener) in listeners.enumerated() {\n                    guard let listener else { continue }\n\n                    group.addTask {\n                        let first = await listener.first(where: { _ in true })\n                        try listener.finish()\n                        return (index, first)\n                    }\n                }\n\n                for try await (index, fileHandle) in group {\n                    results[index] = fileHandle\n                }\n                return results\n            }\n        }\n\n        // Note: stdin relay is started separately via startStdinRelay() after\n        // the process has started, to avoid a deadlock where closeStdin is\n        // called before the process is consuming from the pipe.\n\n        var configuredStreams = 0\n        let (stream, cc) = AsyncStream<Void>.makeStream()\n        if let stdout = self.ioSetup.stdout {\n            configuredStreams += 1\n            handles[1]?.readabilityHandler = { handle in\n                do {\n                    let data = handle.availableData\n                    if data.isEmpty {\n                        // This block is called when the producer (the guest) closes\n                        // the fd it is writing into.\n                        handles[1]?.readabilityHandler = nil\n                        cc.yield()\n                        return\n                    }\n                    try stdout.writer.write(data)\n                } catch {\n                    self.logger?.error(\"failed to write to stdout: \\(error)\")\n                }\n            }\n        }\n\n        if let stderr = self.ioSetup.stderr {\n            configuredStreams += 1\n            handles[2]?.readabilityHandler = { handle in\n                do {\n                    let data = handle.availableData\n                    if data.isEmpty {\n                        handles[2]?.readabilityHandler = nil\n                        cc.yield()\n                        return\n                    }\n                    try stderr.writer.write(data)\n                } catch {\n                    self.logger?.error(\"failed to write to stderr: \\(error)\")\n                }\n            }\n        }\n        if configuredStreams > 0 {\n            self.state.withLock {\n                $0.ioTracker = .init(stream: stream, cont: cc, configuredStreams: configuredStreams)\n            }\n        }\n\n        return handles\n    }\n\n    func startStdinRelay(handle: FileHandle) {\n        guard let stdin = self.ioSetup.stdin else { return }\n\n        self.state.withLock {\n            $0.stdinRelay = Task {\n                for await data in stdin.reader.stream() {\n                    do {\n                        try handle.write(contentsOf: data)\n                    } catch {\n                        self.logger?.error(\"failed to write to stdin: \\(error)\")\n                        break\n                    }\n                }\n\n                do {\n                    self.logger?.debug(\"stdin relay finished, closing\")\n\n                    // There's two ways we can wind up here:\n                    //\n                    // 1. The stream finished on its own (e.g. we wrote all the\n                    // data) and we will close the underlying stdin in the guest below.\n                    //\n                    // 2. The client explicitly called closeStdin() themselves\n                    // which will cancel this relay task AFTER actually closing\n                    // the fds. If the client did that, then this task will be\n                    // cancelled, and the fds are already gone so there's nothing\n                    // for us to do.\n                    if Task.isCancelled {\n                        return\n                    }\n\n                    try await self._closeStdin()\n                } catch {\n                    self.logger?.error(\"failed to close stdin: \\(error)\")\n                }\n            }\n        }\n    }\n\n    /// Start the process.\n    public func start() async throws {\n        do {\n            let spec = self.state.withLock { $0.spec }\n            var listeners = [VsockListener?](repeating: nil, count: 3)\n            if let stdin = self.ioSetup.stdin {\n                listeners[0] = try self.vm.listen(stdin.port)\n            }\n            if let stdout = self.ioSetup.stdout {\n                listeners[1] = try self.vm.listen(stdout.port)\n            }\n            if let stderr = self.ioSetup.stderr {\n                if spec.process!.terminal {\n                    throw ContainerizationError(\n                        .invalidArgument,\n                        message: \"stderr should not be configured with terminal=true\"\n                    )\n                }\n                listeners[2] = try self.vm.listen(stderr.port)\n            }\n\n            let t = Task {\n                try await self.setupIO(listeners: listeners)\n            }\n\n            try await agent.createProcess(\n                id: self.id,\n                containerID: self.owningContainer,\n                stdinPort: self.ioSetup.stdin?.port,\n                stdoutPort: self.ioSetup.stdout?.port,\n                stderrPort: self.ioSetup.stderr?.port,\n                ociRuntimePath: self.ociRuntimePath,\n                configuration: spec,\n                options: nil\n            )\n\n            let result = try await t.value\n            let pid = try await self.agent.startProcess(\n                id: self.id,\n                containerID: self.owningContainer\n            )\n\n            // Start stdin relay after process launch to avoid filling the pipe\n            // buffer before the process is even running.\n            if let stdinHandle = result[0] {\n                self.startStdinRelay(handle: stdinHandle)\n            }\n\n            self.state.withLock {\n                $0.stdio = StdioHandles(\n                    stdin: result[0],\n                    stdout: result[1],\n                    stderr: result[2]\n                )\n                $0.pid = pid\n            }\n        } catch {\n            if let err = error as? ContainerizationError {\n                throw err\n            }\n            throw ContainerizationError(\n                .internalError,\n                message: \"failed to start process\",\n                cause: error,\n            )\n        }\n    }\n\n    /// Kill the process with the specified signal.\n    public func kill(_ signal: Int32) async throws {\n        do {\n            try await agent.signalProcess(\n                id: self.id,\n                containerID: self.owningContainer,\n                signal: signal\n            )\n        } catch {\n            throw ContainerizationError(\n                .internalError,\n                message: \"failed to kill process\",\n                cause: error\n            )\n        }\n    }\n\n    /// Resize the processes pty (if requested).\n    public func resize(to: Terminal.Size) async throws {\n        do {\n            try await agent.resizeProcess(\n                id: self.id,\n                containerID: self.owningContainer,\n                columns: UInt32(to.width),\n                rows: UInt32(to.height)\n            )\n        } catch {\n            throw ContainerizationError(\n                .internalError,\n                message: \"failed to resize process\",\n                cause: error\n            )\n        }\n    }\n\n    public func closeStdin() async throws {\n        do {\n            try await self._closeStdin()\n            self.state.withLock {\n                $0.stdinRelay?.cancel()\n            }\n        } catch {\n            throw ContainerizationError(\n                .internalError,\n                message: \"failed to close stdin\",\n                cause: error,\n            )\n        }\n    }\n\n    func _closeStdin() async throws {\n        try await self.agent.closeProcessStdin(\n            id: self.id,\n            containerID: self.owningContainer\n        )\n    }\n\n    /// Wait on the process to exit with an optional timeout. Returns the exit code of the process.\n    @discardableResult\n    public func wait(timeoutInSeconds: Int64? = nil) async throws -> ExitStatus {\n        do {\n            let exitStatus = try await self.agent.waitProcess(\n                id: self.id,\n                containerID: self.owningContainer,\n                timeoutInSeconds: timeoutInSeconds\n            )\n            await self.waitIoComplete()\n            return exitStatus\n        } catch {\n            if error is ContainerizationError {\n                throw error\n            }\n            throw ContainerizationError(\n                .internalError,\n                message: \"failed to wait on process\",\n                cause: error\n            )\n        }\n    }\n\n    /// Wait until the standard output and standard error streams for the process have concluded.\n    private func waitIoComplete() async {\n        let ioTracker = self.state.withLock { $0.ioTracker }\n        guard let ioTracker else {\n            return\n        }\n        do {\n            try await Timeout.run(seconds: 3) {\n                var counter = ioTracker.configuredStreams\n                for await _ in ioTracker.stream {\n                    counter -= 1\n                    if counter == 0 {\n                        ioTracker.cont.finish()\n                        break\n                    }\n                }\n            }\n        } catch {\n            self.logger?.error(\"timeout waiting for IO to complete for process \\(id): \\(error)\")\n        }\n        self.state.withLock {\n            $0.ioTracker = nil\n        }\n    }\n\n    /// Cleans up guest state and waits on and closes any host resources (stdio handles).\n    public func delete() async throws {\n        try await self._delete()\n        await self.onDelete?()\n    }\n\n    func _delete() async throws {\n        let task = self.state.withLock { state in\n            if let existingTask = state.deletionTask {\n                // Deletion already in progress or finished.\n                return existingTask\n            }\n\n            let task = Task<Void, Error> {\n                try await self.performDeletion()\n            }\n            state.deletionTask = task\n            return task\n        }\n\n        try await task.value\n    }\n\n    private func performDeletion() async throws {\n        do {\n            try await self.agent.deleteProcess(\n                id: self.id,\n                containerID: self.owningContainer\n            )\n        } catch {\n            self.state.withLock {\n                $0.stdinRelay?.cancel()\n                try? $0.stdio.close()\n            }\n            try? await self.agent.close()\n            throw ContainerizationError(\n                .internalError,\n                message: \"failed to delete process\",\n                cause: error,\n            )\n        }\n\n        do {\n            try self.state.withLock {\n                $0.stdinRelay?.cancel()\n                try $0.stdio.close()\n            }\n        } catch {\n            try? await self.agent.close()\n            throw ContainerizationError(\n                .internalError,\n                message: \"failed to close stdio\",\n                cause: error,\n            )\n        }\n\n        do {\n            try await self.agent.close()\n        } catch {\n            throw ContainerizationError(\n                .internalError,\n                message: \"failed to close agent connection\",\n                cause: error,\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/LinuxProcessConfiguration.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport ContainerizationOCI\nimport ContainerizationOS\n\n/// A resource limit (rlimit) configuration for a container process.\npublic struct LinuxRLimit: Sendable, Hashable {\n    /// The kind of resource limit.\n    public var kind: Kind\n    /// The hard limit value.\n    public var hard: UInt64\n    /// The soft limit value.\n    public var soft: UInt64\n\n    /// Creates a new resource limit.\n    ///\n    /// - Parameters:\n    ///   - kind: The kind of resource limit.\n    ///   - hard: The hard limit value.\n    ///   - soft: The soft limit value.\n    public init(kind: Kind, hard: UInt64, soft: UInt64) {\n        self.kind = kind\n        self.hard = hard\n        self.soft = soft\n    }\n\n    /// Creates a new resource limit with the same value for both hard and soft limits.\n    ///\n    /// - Parameters:\n    ///   - kind: The kind of resource limit.\n    ///   - limit: The limit value for both hard and soft limits.\n    public init(kind: Kind, limit: UInt64) {\n        self.kind = kind\n        self.hard = limit\n        self.soft = limit\n    }\n\n    /// Convert to OCI POSIXRlimit format for transport.\n    public func toOCI() -> POSIXRlimit {\n        POSIXRlimit(type: self.kind.description, hard: self.hard, soft: self.soft)\n    }\n}\n\nextension LinuxRLimit {\n    /// The kind of resource limit.\n    public struct Kind: Sendable, Hashable {\n        private enum Value: Hashable, Sendable, CaseIterable {\n            case addressSpace\n            case coreFileSize\n            case cpuTime\n            case dataSize\n            case fileSize\n            case locks\n            case lockedMemory\n            case messageQueue\n            case nice\n            case openFiles\n            case numberOfProcesses\n            case residentSetSize\n            case realtimePriority\n            case realtimeTimeout\n            case signalsPending\n            case stackSize\n        }\n\n        private var value: Value\n        private init(_ value: Value) {\n            self.value = value\n        }\n\n        /// Maximum size of the process's virtual memory (address space) in bytes.\n        public static var addressSpace: Self {\n            Self(.addressSpace)\n        }\n\n        /// Maximum size of a core file in bytes.\n        public static var coreFileSize: Self {\n            Self(.coreFileSize)\n        }\n\n        /// Maximum amount of CPU time the process can consume in seconds.\n        public static var cpuTime: Self {\n            Self(.cpuTime)\n        }\n\n        /// Maximum size of the process's data segment in bytes.\n        public static var dataSize: Self {\n            Self(.dataSize)\n        }\n\n        /// Maximum size of files the process may create in bytes.\n        public static var fileSize: Self {\n            Self(.fileSize)\n        }\n\n        /// Maximum number of file locks.\n        public static var locks: Self {\n            Self(.locks)\n        }\n\n        /// Maximum number of bytes of memory that may be locked into RAM.\n        public static var lockedMemory: Self {\n            Self(.lockedMemory)\n        }\n\n        /// Maximum number of bytes that can be allocated for POSIX message queues.\n        public static var messageQueue: Self {\n            Self(.messageQueue)\n        }\n\n        /// Maximum nice value that can be set.\n        public static var nice: Self {\n            Self(.nice)\n        }\n\n        /// Maximum number of open file descriptors.\n        public static var openFiles: Self {\n            Self(.openFiles)\n        }\n\n        /// Maximum number of processes that can be created by the user.\n        public static var numberOfProcesses: Self {\n            Self(.numberOfProcesses)\n        }\n\n        /// Maximum size of the process's resident set (physical memory) in bytes.\n        public static var residentSetSize: Self {\n            Self(.residentSetSize)\n        }\n\n        /// Maximum real-time scheduling priority.\n        public static var realtimePriority: Self {\n            Self(.realtimePriority)\n        }\n\n        /// Maximum amount of CPU time for real-time scheduling in microseconds.\n        public static var realtimeTimeout: Self {\n            Self(.realtimeTimeout)\n        }\n\n        /// Maximum number of signals that may be queued.\n        public static var signalsPending: Self {\n            Self(.signalsPending)\n        }\n\n        /// Maximum size of the process stack in bytes.\n        public static var stackSize: Self {\n            Self(.stackSize)\n        }\n\n        /// Creates a Kind from its OCI string representation.\n        ///\n        /// - Parameter string: The OCI string representation (e.g., \"RLIMIT_NOFILE\").\n        /// - Throws: `ContainerizationError` with code `.invalidArgument` if the string doesn't match a known rlimit kind.\n        public init(_ string: String) throws {\n            switch string {\n            case \"RLIMIT_AS\":\n                self = .addressSpace\n            case \"RLIMIT_CORE\":\n                self = .coreFileSize\n            case \"RLIMIT_CPU\":\n                self = .cpuTime\n            case \"RLIMIT_DATA\":\n                self = .dataSize\n            case \"RLIMIT_FSIZE\":\n                self = .fileSize\n            case \"RLIMIT_LOCKS\":\n                self = .locks\n            case \"RLIMIT_MEMLOCK\":\n                self = .lockedMemory\n            case \"RLIMIT_MSGQUEUE\":\n                self = .messageQueue\n            case \"RLIMIT_NICE\":\n                self = .nice\n            case \"RLIMIT_NOFILE\":\n                self = .openFiles\n            case \"RLIMIT_NPROC\":\n                self = .numberOfProcesses\n            case \"RLIMIT_RSS\":\n                self = .residentSetSize\n            case \"RLIMIT_RTPRIO\":\n                self = .realtimePriority\n            case \"RLIMIT_RTTIME\":\n                self = .realtimeTimeout\n            case \"RLIMIT_SIGPENDING\":\n                self = .signalsPending\n            case \"RLIMIT_STACK\":\n                self = .stackSize\n            default:\n                throw ContainerizationError(.invalidArgument, message: \"invalid rlimit kind: '\\(string)'\")\n            }\n        }\n    }\n}\n\nextension LinuxRLimit.Kind: CustomStringConvertible {\n    /// The OCI string representation of the resource limit kind.\n    public var description: String {\n        switch self.value {\n        case .addressSpace:\n            \"RLIMIT_AS\"\n        case .coreFileSize:\n            \"RLIMIT_CORE\"\n        case .cpuTime:\n            \"RLIMIT_CPU\"\n        case .dataSize:\n            \"RLIMIT_DATA\"\n        case .fileSize:\n            \"RLIMIT_FSIZE\"\n        case .locks:\n            \"RLIMIT_LOCKS\"\n        case .lockedMemory:\n            \"RLIMIT_MEMLOCK\"\n        case .messageQueue:\n            \"RLIMIT_MSGQUEUE\"\n        case .nice:\n            \"RLIMIT_NICE\"\n        case .openFiles:\n            \"RLIMIT_NOFILE\"\n        case .numberOfProcesses:\n            \"RLIMIT_NPROC\"\n        case .residentSetSize:\n            \"RLIMIT_RSS\"\n        case .realtimePriority:\n            \"RLIMIT_RTPRIO\"\n        case .realtimeTimeout:\n            \"RLIMIT_RTTIME\"\n        case .signalsPending:\n            \"RLIMIT_SIGPENDING\"\n        case .stackSize:\n            \"RLIMIT_STACK\"\n        }\n    }\n}\n\n/// User-friendly Linux capabilities configuration\npublic struct LinuxCapabilities: Sendable {\n    /// Capabilities that define the maximum set of capabilities a process can have\n    public var bounding: [CapabilityName] = []\n    /// Capabilities that are actually in effect for the current process\n    public var effective: [CapabilityName] = []\n    /// Capabilities that can be inherited by child processes\n    public var inheritable: [CapabilityName] = []\n    /// Capabilities that are currently permitted for the process\n    public var permitted: [CapabilityName] = []\n    /// Capabilities that are preserved across execve() calls\n    public var ambient: [CapabilityName] = []\n\n    /// Grant all capabilities\n    public static let allCapabilities = LinuxCapabilities(\n        bounding: CapabilityName.allCases,\n        effective: CapabilityName.allCases,\n        inheritable: CapabilityName.allCases,\n        permitted: CapabilityName.allCases,\n        ambient: CapabilityName.allCases\n    )\n\n    /// Default configuration\n    public static let defaultOCICapabilities = LinuxCapabilities(\n        bounding: [\n            .chown,\n            .dacOverride,\n            .fsetid,\n            .fowner,\n            .mknod,\n            .netRaw,\n            .setgid,\n            .setuid,\n            .setfcap,\n            .setpcap,\n            .netBindService,\n            .sysChroot,\n            .kill,\n            .auditWrite,\n        ],\n        effective: [\n            .chown,\n            .dacOverride,\n            .fsetid,\n            .fowner,\n            .mknod,\n            .netRaw,\n            .setgid,\n            .setuid,\n            .setfcap,\n            .setpcap,\n            .netBindService,\n            .sysChroot,\n            .kill,\n            .auditWrite,\n        ],\n        permitted: [\n            .chown,\n            .dacOverride,\n            .fsetid,\n            .fowner,\n            .mknod,\n            .netRaw,\n            .setgid,\n            .setuid,\n            .setfcap,\n            .setpcap,\n            .netBindService,\n            .sysChroot,\n            .kill,\n            .auditWrite,\n        ],\n    )\n\n    public init(\n        bounding: [CapabilityName] = [],\n        effective: [CapabilityName] = [],\n        inheritable: [CapabilityName] = [],\n        permitted: [CapabilityName] = [],\n        ambient: [CapabilityName] = []\n    ) {\n        self.bounding = bounding\n        self.effective = effective\n        self.inheritable = inheritable\n        self.permitted = permitted\n        self.ambient = ambient\n    }\n\n    /// Convenience initializer that sets the same capabilities to effective, permitted, and bounding sets\n    /// This matches the typical pattern used by containerd/runc\n    public init(capabilities: [CapabilityName]) {\n        self.bounding = capabilities\n        self.effective = capabilities\n        self.inheritable = []\n        self.permitted = capabilities\n        self.ambient = []\n    }\n\n    /// Convert to OCI format for transport\n    public func toOCI() -> ContainerizationOCI.LinuxCapabilities {\n        ContainerizationOCI.LinuxCapabilities(\n            bounding: bounding.isEmpty ? nil : bounding.map { $0.description },\n            effective: effective.isEmpty ? nil : effective.map { $0.description },\n            inheritable: inheritable.isEmpty ? nil : inheritable.map { $0.description },\n            permitted: permitted.isEmpty ? nil : permitted.map { $0.description },\n            ambient: ambient.isEmpty ? nil : ambient.map { $0.description }\n        )\n    }\n}\n\npublic struct LinuxProcessConfiguration: Sendable {\n    /// The default PATH value for a process.\n    public static let defaultPath = \"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"\n\n    /// The arguments for the container process.\n    public var arguments: [String] = []\n    /// The environment variables for the container process.\n    public var environmentVariables: [String] = [\"PATH=\\(Self.defaultPath)\"]\n    /// The working directory for the container process.\n    public var workingDirectory: String = \"/\"\n    /// The user the container process will run as.\n    public var user: ContainerizationOCI.User = .init()\n    /// The rlimits for the container process.\n    public var rlimits: [LinuxRLimit] = []\n    /// Whether to set the no_new_privileges bit on the container process. When true, the\n    /// process and its children cannot gain additional privileges via setuid/setgid binaries\n    /// or file capabilities.\n    public var noNewPrivileges: Bool = false\n    /// The Linux capabilities for the container process.\n    public var capabilities: LinuxCapabilities = .allCapabilities\n    /// Whether to allocate a pseudo terminal for the process. If you'd like interactive\n    /// behavior and are planning to use a terminal for stdin/out/err on the client side,\n    /// this should likely be set to true.\n    public var terminal: Bool = false\n    /// The stdin for the process.\n    public var stdin: ReaderStream?\n    /// The stdout for the process.\n    public var stdout: Writer?\n    /// The stderr for the process.\n    public var stderr: Writer?\n\n    public init() {}\n\n    public init(\n        arguments: [String],\n        environmentVariables: [String] = [\"PATH=\\(Self.defaultPath)\"],\n        workingDirectory: String = \"/\",\n        user: ContainerizationOCI.User = .init(),\n        rlimits: [LinuxRLimit] = [],\n        noNewPrivileges: Bool = false,\n        capabilities: LinuxCapabilities = .allCapabilities,\n        terminal: Bool = false,\n        stdin: ReaderStream? = nil,\n        stdout: Writer? = nil,\n        stderr: Writer? = nil\n    ) {\n        self.arguments = arguments\n        self.environmentVariables = environmentVariables\n        self.workingDirectory = workingDirectory\n        self.user = user\n        self.rlimits = rlimits\n        self.noNewPrivileges = noNewPrivileges\n        self.capabilities = capabilities\n        self.terminal = terminal\n        self.stdin = stdin\n        self.stdout = stdout\n        self.stderr = stderr\n    }\n\n    public init(from config: ImageConfig) {\n        self.workingDirectory = config.workingDir ?? \"/\"\n        self.environmentVariables = config.env ?? []\n        self.arguments = (config.entrypoint ?? []) + (config.cmd ?? [])\n        self.user = {\n            if let rawString = config.user {\n                return User(username: rawString)\n            }\n            return User()\n        }()\n    }\n\n    /// Sets up IO to be handled by the passed in Terminal, and edits the\n    /// process configuration to set the necessary state for using a pty.\n    mutating public func setTerminalIO(terminal: Terminal) {\n        self.environmentVariables.append(\"TERM=xterm\")\n        self.terminal = true\n        self.stdin = terminal\n        self.stdout = terminal\n    }\n\n    func toOCI() -> ContainerizationOCI.Process {\n        ContainerizationOCI.Process(\n            args: self.arguments,\n            cwd: self.workingDirectory,\n            env: self.environmentVariables,\n            noNewPrivileges: self.noNewPrivileges,\n            capabilities: self.capabilities.toOCI(),\n            user: self.user,\n            rlimits: self.rlimits.map { $0.toOCI() },\n            terminal: self.terminal\n        )\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/Mount.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(macOS)\nimport Foundation\nimport Virtualization\nimport ContainerizationError\n#endif\n\n/// A filesystem mount exposed to a container.\npublic struct Mount: Sendable {\n    /// The filesystem or mount type. This is the string\n    /// that will be used for the mount syscall itself.\n    public var type: String\n    /// The source path of the mount.\n    public var source: String\n    /// The destination path of the mount.\n    public var destination: String\n    /// Filesystem or mount specific options.\n    public var options: [String]\n    /// Runtime specific options. This can be used\n    /// as a way to discern what kind of device a vmm\n    /// should create for this specific mount (virtioblock\n    /// virtiofs etc.).\n    public let runtimeOptions: RuntimeOptions\n\n    /// A type representing a \"hint\" of what type\n    /// of mount this really is (block, directory, purely\n    /// guest mount) and a set of type specific options, if any.\n    public enum RuntimeOptions: Sendable {\n        case virtioblk([String])\n        case virtiofs([String])\n        case any([String])\n    }\n\n    public init(\n        type: String,\n        source: String,\n        destination: String,\n        options: [String],\n        runtimeOptions: RuntimeOptions\n    ) {\n        self.type = type\n        self.source = source\n        self.destination = destination\n        self.options = options\n        self.runtimeOptions = runtimeOptions\n    }\n\n    /// Mount representing a virtio block device.\n    public static func block(\n        format: String,\n        source: String,\n        destination: String,\n        options: [String] = [],\n        runtimeOptions: [String] = []\n    ) -> Self {\n        .init(\n            type: format,\n            source: source,\n            destination: destination,\n            options: options,\n            runtimeOptions: .virtioblk(runtimeOptions)\n        )\n    }\n\n    /// Mount representing a virtiofs share.\n    public static func share(\n        source: String,\n        destination: String,\n        options: [String] = [],\n        runtimeOptions: [String] = []\n    ) -> Self {\n        .init(\n            type: \"virtiofs\",\n            source: source,\n            destination: destination,\n            options: options,\n            runtimeOptions: .virtiofs(runtimeOptions)\n        )\n    }\n\n    /// A generic mount.\n    public static func any(\n        type: String,\n        source: String,\n        destination: String,\n        options: [String] = [],\n        runtimeOptions: [String] = []\n    ) -> Self {\n        .init(\n            type: type,\n            source: source,\n            destination: destination,\n            options: options,\n            runtimeOptions: .any(runtimeOptions)\n        )\n    }\n\n    #if os(macOS)\n    /// Clone the Mount to the provided path.\n    ///\n    /// This uses `clonefile` to provide a copy-on-write copy of the Mount.\n    public func clone(to: String) throws -> Self {\n        let fm = FileManager.default\n        let src = self.source\n        try fm.copyItem(atPath: src, toPath: to)\n\n        return .init(\n            type: self.type,\n            source: to,\n            destination: self.destination,\n            options: self.options,\n            runtimeOptions: self.runtimeOptions\n        )\n    }\n    #endif\n}\n\n#if os(macOS)\n\nextension Mount {\n    func configure(config: inout VZVirtualMachineConfiguration) throws {\n        switch self.runtimeOptions {\n        case .virtioblk(let options):\n            let device = try VZDiskImageStorageDeviceAttachment.mountToVZAttachment(mount: self, options: options)\n            let attachment = VZVirtioBlockDeviceConfiguration(attachment: device)\n            config.storageDevices.append(attachment)\n        case .virtiofs(_):\n            guard FileManager.default.fileExists(atPath: self.source) else {\n                throw ContainerizationError(.notFound, message: \"directory \\(source) does not exist\")\n            }\n\n            let name = try hashMountSource(source: self.source)\n            let urlSource = URL(fileURLWithPath: source)\n\n            let device = VZVirtioFileSystemDeviceConfiguration(tag: name)\n            device.share = VZSingleDirectoryShare(\n                directory: VZSharedDirectory(\n                    url: urlSource,\n                    readOnly: readonly\n                )\n            )\n            config.directorySharingDevices.append(device)\n        case .any:\n            break\n        }\n    }\n}\n\nextension VZDiskImageStorageDeviceAttachment {\n    static func mountToVZAttachment(mount: Mount, options: [String]) throws -> VZDiskImageStorageDeviceAttachment {\n        var synchronizationMode: VZDiskImageSynchronizationMode = .fsync\n        var cachingMode: VZDiskImageCachingMode = .cached\n\n        for option in options {\n            let split = option.split(separator: \"=\")\n            if split.count != 2 {\n                continue\n            }\n\n            let key = String(split[0])\n            let value = String(split[1])\n\n            switch key {\n            case \"vzDiskImageCachingMode\":\n                switch value {\n                case \"automatic\":\n                    cachingMode = .automatic\n                case \"cached\":\n                    cachingMode = .cached\n                case \"uncached\":\n                    cachingMode = .uncached\n                default:\n                    throw ContainerizationError(\n                        .invalidArgument,\n                        message: \"unknown vzDiskImageCachingMode value for virtio block device: \\(value)\"\n                    )\n                }\n            case \"vzDiskImageSynchronizationMode\":\n                switch value {\n                case \"full\":\n                    synchronizationMode = .full\n                case \"fsync\":\n                    synchronizationMode = .fsync\n                case \"none\":\n                    synchronizationMode = .none\n                default:\n                    throw ContainerizationError(\n                        .invalidArgument,\n                        message: \"unknown vzDiskImageSynchronizationMode value for virtio block device: \\(value)\"\n                    )\n                }\n            default:\n                throw ContainerizationError(\n                    .invalidArgument,\n                    message: \"unknown vmm option encountered: \\(key)\"\n                )\n            }\n        }\n        return try VZDiskImageStorageDeviceAttachment(\n            url: URL(filePath: mount.source),\n            readOnly: mount.readonly,\n            cachingMode: cachingMode,\n            synchronizationMode: synchronizationMode\n        )\n    }\n}\n\n#endif\n\nextension Mount {\n    fileprivate var readonly: Bool {\n        self.options.contains(\"ro\")\n    }\n\n    /// Returns true if this mount is a virtio block device.\n    public var isBlock: Bool {\n        if case .virtioblk = self.runtimeOptions {\n            return true\n        }\n        return false\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/NATInterface.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationExtras\n\npublic struct NATInterface: Interface {\n    public var ipv4Address: CIDRv4\n    public var ipv4Gateway: IPv4Address?\n    public var macAddress: MACAddress?\n    public var mtu: UInt32\n\n    public init(ipv4Address: CIDRv4, ipv4Gateway: IPv4Address?, macAddress: MACAddress? = nil, mtu: UInt32 = 1500) {\n        self.ipv4Address = ipv4Address\n        self.ipv4Gateway = ipv4Gateway\n        self.macAddress = macAddress\n        self.mtu = mtu\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/NATNetworkInterface.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(macOS)\n\nimport vmnet\nimport Virtualization\nimport ContainerizationError\nimport ContainerizationExtras\nimport Foundation\nimport Synchronization\n\n/// An interface that uses NAT to provide an IP address for a given\n/// container/virtual machine.\n@available(macOS 26, *)\npublic final class NATNetworkInterface: Interface, Sendable {\n    public let ipv4Address: CIDRv4\n    public let ipv4Gateway: IPv4Address?\n    public let macAddress: MACAddress?\n    public let mtu: UInt32\n\n    @available(macOS 26, *)\n    // `reference` isn't used concurrently.\n    public nonisolated(unsafe) let reference: vmnet_network_ref!\n\n    @available(macOS 26, *)\n    public init(\n        ipv4Address: CIDRv4,\n        ipv4Gateway: IPv4Address?,\n        reference: sending vmnet_network_ref,\n        macAddress: MACAddress? = nil,\n        mtu: UInt32 = 1500\n    ) {\n        self.ipv4Address = ipv4Address\n        self.ipv4Gateway = ipv4Gateway\n        self.macAddress = macAddress\n        self.mtu = mtu\n        self.reference = reference\n    }\n\n    @available(macOS, obsoleted: 26, message: \"Use init(ipv4Address:ipv4Gateway:reference:macAddress:) instead\")\n    public init(\n        ipv4Address: CIDRv4,\n        ipv4Gateway: IPv4Address?,\n        macAddress: MACAddress? = nil,\n        mtu: UInt32 = 1500\n    ) {\n        self.ipv4Address = ipv4Address\n        self.ipv4Gateway = ipv4Gateway\n        self.macAddress = macAddress\n        self.mtu = mtu\n        self.reference = nil\n    }\n}\n\n@available(macOS 26, *)\nextension NATNetworkInterface: VZInterface {\n    public func device() throws -> VZVirtioNetworkDeviceConfiguration {\n        let config = VZVirtioNetworkDeviceConfiguration()\n        if let macAddress = self.macAddress {\n            guard let mac = VZMACAddress(string: macAddress.description) else {\n                throw ContainerizationError(.invalidArgument, message: \"invalid mac address \\(macAddress)\")\n            }\n            config.macAddress = mac\n        }\n\n        config.attachment = VZVmnetNetworkDeviceAttachment(network: self.reference)\n        return config\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Containerization/Network.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// A network that can allocate and release interfaces for use with containers.\npublic protocol Network: Sendable {\n    mutating func createInterface(_ id: String) throws -> Interface?\n    mutating func releaseInterface(_ id: String) throws\n}\n"
  },
  {
    "path": "Sources/Containerization/SandboxContext/SandboxContext.grpc.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//\n// DO NOT EDIT.\n// swift-format-ignore-file\n//\n// Generated by the protocol buffer compiler.\n// Source: SandboxContext.proto\n//\nimport GRPC\nimport NIO\nimport NIOConcurrencyHelpers\nimport SwiftProtobuf\n\n\n/// Context for interacting with a container's runtime environment.\n///\n/// Usage: instantiate `Com_Apple_Containerization_Sandbox_V3_SandboxContextClient`, then call methods of this protocol to make API calls.\npublic protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextClientProtocol: GRPCClient {\n  var serviceName: String { get }\n  var interceptors: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientInterceptorFactoryProtocol? { get }\n\n  func mount(\n    _ request: Com_Apple_Containerization_Sandbox_V3_MountRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_MountRequest, Com_Apple_Containerization_Sandbox_V3_MountResponse>\n\n  func umount(\n    _ request: Com_Apple_Containerization_Sandbox_V3_UmountRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_UmountRequest, Com_Apple_Containerization_Sandbox_V3_UmountResponse>\n\n  func setenv(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SetenvRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_SetenvRequest, Com_Apple_Containerization_Sandbox_V3_SetenvResponse>\n\n  func getenv(\n    _ request: Com_Apple_Containerization_Sandbox_V3_GetenvRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_GetenvRequest, Com_Apple_Containerization_Sandbox_V3_GetenvResponse>\n\n  func mkdir(\n    _ request: Com_Apple_Containerization_Sandbox_V3_MkdirRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_MkdirRequest, Com_Apple_Containerization_Sandbox_V3_MkdirResponse>\n\n  func sysctl(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SysctlRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_SysctlRequest, Com_Apple_Containerization_Sandbox_V3_SysctlResponse>\n\n  func setTime(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SetTimeRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_SetTimeRequest, Com_Apple_Containerization_Sandbox_V3_SetTimeResponse>\n\n  func setupEmulator(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest, Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse>\n\n  func writeFile(\n    _ request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, Com_Apple_Containerization_Sandbox_V3_WriteFileResponse>\n\n  func copy(\n    _ request: Com_Apple_Containerization_Sandbox_V3_CopyRequest,\n    callOptions: CallOptions?,\n    handler: @escaping (Com_Apple_Containerization_Sandbox_V3_CopyResponse) -> Void\n  ) -> ServerStreamingCall<Com_Apple_Containerization_Sandbox_V3_CopyRequest, Com_Apple_Containerization_Sandbox_V3_CopyResponse>\n\n  func createProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest, Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse>\n\n  func deleteProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest, Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse>\n\n  func startProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_StartProcessRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_StartProcessRequest, Com_Apple_Containerization_Sandbox_V3_StartProcessResponse>\n\n  func killProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_KillProcessRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_KillProcessRequest, Com_Apple_Containerization_Sandbox_V3_KillProcessResponse>\n\n  func waitProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest, Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse>\n\n  func resizeProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest, Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse>\n\n  func closeProcessStdin(\n    _ request: Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest, Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse>\n\n  func containerStatistics(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest, Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse>\n\n  func proxyVsock(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest, Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse>\n\n  func stopVsockProxy(\n    _ request: Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest, Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse>\n\n  func ipLinkSet(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest, Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse>\n\n  func ipAddrAdd(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest, Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse>\n\n  func ipRouteAddLink(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest, Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse>\n\n  func ipRouteAddDefault(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest, Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse>\n\n  func configureDns(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest, Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse>\n\n  func configureHosts(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest, Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse>\n\n  func sync(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SyncRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_SyncRequest, Com_Apple_Containerization_Sandbox_V3_SyncResponse>\n\n  func kill(\n    _ request: Com_Apple_Containerization_Sandbox_V3_KillRequest,\n    callOptions: CallOptions?\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_KillRequest, Com_Apple_Containerization_Sandbox_V3_KillResponse>\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_SandboxContextClientProtocol {\n  public var serviceName: String {\n    return \"com.apple.containerization.sandbox.v3.SandboxContext\"\n  }\n\n  /// Mount a filesystem.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to Mount.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func mount(\n    _ request: Com_Apple_Containerization_Sandbox_V3_MountRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_MountRequest, Com_Apple_Containerization_Sandbox_V3_MountResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.mount.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeMountInterceptors() ?? []\n    )\n  }\n\n  /// Unmount a filesystem.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to Umount.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func umount(\n    _ request: Com_Apple_Containerization_Sandbox_V3_UmountRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_UmountRequest, Com_Apple_Containerization_Sandbox_V3_UmountResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.umount.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeUmountInterceptors() ?? []\n    )\n  }\n\n  /// Set an environment variable on the init process.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to Setenv.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func setenv(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SetenvRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_SetenvRequest, Com_Apple_Containerization_Sandbox_V3_SetenvResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.setenv.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeSetenvInterceptors() ?? []\n    )\n  }\n\n  /// Get an environment variable from the init process.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to Getenv.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func getenv(\n    _ request: Com_Apple_Containerization_Sandbox_V3_GetenvRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_GetenvRequest, Com_Apple_Containerization_Sandbox_V3_GetenvResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.getenv.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeGetenvInterceptors() ?? []\n    )\n  }\n\n  /// Create a new directory inside the sandbox.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to Mkdir.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func mkdir(\n    _ request: Com_Apple_Containerization_Sandbox_V3_MkdirRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_MkdirRequest, Com_Apple_Containerization_Sandbox_V3_MkdirResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.mkdir.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeMkdirInterceptors() ?? []\n    )\n  }\n\n  /// Set sysctls in the context of the sandbox.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to Sysctl.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func sysctl(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SysctlRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_SysctlRequest, Com_Apple_Containerization_Sandbox_V3_SysctlResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.sysctl.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeSysctlInterceptors() ?? []\n    )\n  }\n\n  /// Set time in the guest.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to SetTime.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func setTime(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SetTimeRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_SetTimeRequest, Com_Apple_Containerization_Sandbox_V3_SetTimeResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.setTime.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeSetTimeInterceptors() ?? []\n    )\n  }\n\n  /// Set up an emulator in the guest for a specific binary format.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to SetupEmulator.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func setupEmulator(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest, Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.setupEmulator.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeSetupEmulatorInterceptors() ?? []\n    )\n  }\n\n  /// Write data to an existing or new file.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to WriteFile.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func writeFile(\n    _ request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, Com_Apple_Containerization_Sandbox_V3_WriteFileResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.writeFile.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeWriteFileInterceptors() ?? []\n    )\n  }\n\n  /// Copy a file or directory between the host and guest.\n  /// Data transfer happens over a dedicated vsock connection;\n  /// the gRPC stream is used only for control/metadata.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to Copy.\n  ///   - callOptions: Call options.\n  ///   - handler: A closure called when each response is received from the server.\n  /// - Returns: A `ServerStreamingCall` with futures for the metadata and status.\n  public func copy(\n    _ request: Com_Apple_Containerization_Sandbox_V3_CopyRequest,\n    callOptions: CallOptions? = nil,\n    handler: @escaping (Com_Apple_Containerization_Sandbox_V3_CopyResponse) -> Void\n  ) -> ServerStreamingCall<Com_Apple_Containerization_Sandbox_V3_CopyRequest, Com_Apple_Containerization_Sandbox_V3_CopyResponse> {\n    return self.makeServerStreamingCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.copy.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeCopyInterceptors() ?? [],\n      handler: handler\n    )\n  }\n\n  /// Create a new process inside the container.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to CreateProcess.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func createProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest, Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.createProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeCreateProcessInterceptors() ?? []\n    )\n  }\n\n  /// Delete an existing process inside the container.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to DeleteProcess.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func deleteProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest, Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.deleteProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeDeleteProcessInterceptors() ?? []\n    )\n  }\n\n  /// Start the provided process.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to StartProcess.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func startProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_StartProcessRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_StartProcessRequest, Com_Apple_Containerization_Sandbox_V3_StartProcessResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.startProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeStartProcessInterceptors() ?? []\n    )\n  }\n\n  /// Send a signal to the provided process.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to KillProcess.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func killProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_KillProcessRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_KillProcessRequest, Com_Apple_Containerization_Sandbox_V3_KillProcessResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.killProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeKillProcessInterceptors() ?? []\n    )\n  }\n\n  /// Wait for a process to exit and return the exit code.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to WaitProcess.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func waitProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest, Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.waitProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeWaitProcessInterceptors() ?? []\n    )\n  }\n\n  /// Resize the tty of a given process. This will error if the process does\n  /// not have a pty allocated.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to ResizeProcess.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func resizeProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest, Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.resizeProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeResizeProcessInterceptors() ?? []\n    )\n  }\n\n  /// Close IO for a given process.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to CloseProcessStdin.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func closeProcessStdin(\n    _ request: Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest, Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.closeProcessStdin.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeCloseProcessStdinInterceptors() ?? []\n    )\n  }\n\n  /// Get statistics for containers.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to ContainerStatistics.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func containerStatistics(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest, Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.containerStatistics.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeContainerStatisticsInterceptors() ?? []\n    )\n  }\n\n  /// Proxy a vsock port to a unix domain socket in the guest, or vice versa.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to ProxyVsock.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func proxyVsock(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest, Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.proxyVsock.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeProxyVsockInterceptors() ?? []\n    )\n  }\n\n  /// Stop a vsock proxy to a unix domain socket.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to StopVsockProxy.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func stopVsockProxy(\n    _ request: Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest, Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.stopVsockProxy.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeStopVsockProxyInterceptors() ?? []\n    )\n  }\n\n  /// Set the link state of a network interface.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to IpLinkSet.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func ipLinkSet(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest, Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipLinkSet.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeIpLinkSetInterceptors() ?? []\n    )\n  }\n\n  /// Add an IPv4 address to a network interface.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to IpAddrAdd.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func ipAddrAdd(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest, Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipAddrAdd.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeIpAddrAddInterceptors() ?? []\n    )\n  }\n\n  /// Add an IP route for a network interface.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to IpRouteAddLink.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func ipRouteAddLink(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest, Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipRouteAddLink.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeIpRouteAddLinkInterceptors() ?? []\n    )\n  }\n\n  /// Add an IP route for a network interface.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to IpRouteAddDefault.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func ipRouteAddDefault(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest, Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipRouteAddDefault.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeIpRouteAddDefaultInterceptors() ?? []\n    )\n  }\n\n  /// Configure DNS resolver.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to ConfigureDns.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func configureDns(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest, Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.configureDns.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeConfigureDnsInterceptors() ?? []\n    )\n  }\n\n  /// Configure /etc/hosts.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to ConfigureHosts.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func configureHosts(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest, Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.configureHosts.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeConfigureHostsInterceptors() ?? []\n    )\n  }\n\n  /// Perform the sync syscall.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to Sync.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func sync(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SyncRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_SyncRequest, Com_Apple_Containerization_Sandbox_V3_SyncResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.sync.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeSyncInterceptors() ?? []\n    )\n  }\n\n  /// Send a signal to a process via the PID.\n  ///\n  /// - Parameters:\n  ///   - request: Request to send to Kill.\n  ///   - callOptions: Call options.\n  /// - Returns: A `UnaryCall` with futures for the metadata, status and response.\n  public func kill(\n    _ request: Com_Apple_Containerization_Sandbox_V3_KillRequest,\n    callOptions: CallOptions? = nil\n  ) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_KillRequest, Com_Apple_Containerization_Sandbox_V3_KillResponse> {\n    return self.makeUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.kill.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeKillInterceptors() ?? []\n    )\n  }\n}\n\n@available(*, deprecated)\nextension Com_Apple_Containerization_Sandbox_V3_SandboxContextClient: @unchecked Sendable {}\n\n@available(*, deprecated, renamed: \"Com_Apple_Containerization_Sandbox_V3_SandboxContextNIOClient\")\npublic final class Com_Apple_Containerization_Sandbox_V3_SandboxContextClient: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientProtocol {\n  private let lock = Lock()\n  private var _defaultCallOptions: CallOptions\n  private var _interceptors: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientInterceptorFactoryProtocol?\n  public let channel: GRPCChannel\n  public var defaultCallOptions: CallOptions {\n    get { self.lock.withLock { return self._defaultCallOptions } }\n    set { self.lock.withLockVoid { self._defaultCallOptions = newValue } }\n  }\n  public var interceptors: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientInterceptorFactoryProtocol? {\n    get { self.lock.withLock { return self._interceptors } }\n    set { self.lock.withLockVoid { self._interceptors = newValue } }\n  }\n\n  /// Creates a client for the com.apple.containerization.sandbox.v3.SandboxContext service.\n  ///\n  /// - Parameters:\n  ///   - channel: `GRPCChannel` to the service host.\n  ///   - defaultCallOptions: Options to use for each service call if the user doesn't provide them.\n  ///   - interceptors: A factory providing interceptors for each RPC.\n  public init(\n    channel: GRPCChannel,\n    defaultCallOptions: CallOptions = CallOptions(),\n    interceptors: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientInterceptorFactoryProtocol? = nil\n  ) {\n    self.channel = channel\n    self._defaultCallOptions = defaultCallOptions\n    self._interceptors = interceptors\n  }\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_SandboxContextNIOClient: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientProtocol {\n  public var channel: GRPCChannel\n  public var defaultCallOptions: CallOptions\n  public var interceptors: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientInterceptorFactoryProtocol?\n\n  /// Creates a client for the com.apple.containerization.sandbox.v3.SandboxContext service.\n  ///\n  /// - Parameters:\n  ///   - channel: `GRPCChannel` to the service host.\n  ///   - defaultCallOptions: Options to use for each service call if the user doesn't provide them.\n  ///   - interceptors: A factory providing interceptors for each RPC.\n  public init(\n    channel: GRPCChannel,\n    defaultCallOptions: CallOptions = CallOptions(),\n    interceptors: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientInterceptorFactoryProtocol? = nil\n  ) {\n    self.channel = channel\n    self.defaultCallOptions = defaultCallOptions\n    self.interceptors = interceptors\n  }\n}\n\n/// Context for interacting with a container's runtime environment.\n@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)\npublic protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncClientProtocol: GRPCClient {\n  static var serviceDescriptor: GRPCServiceDescriptor { get }\n  var interceptors: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientInterceptorFactoryProtocol? { get }\n\n  func makeMountCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_MountRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_MountRequest, Com_Apple_Containerization_Sandbox_V3_MountResponse>\n\n  func makeUmountCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_UmountRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_UmountRequest, Com_Apple_Containerization_Sandbox_V3_UmountResponse>\n\n  func makeSetenvCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SetenvRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_SetenvRequest, Com_Apple_Containerization_Sandbox_V3_SetenvResponse>\n\n  func makeGetenvCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_GetenvRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_GetenvRequest, Com_Apple_Containerization_Sandbox_V3_GetenvResponse>\n\n  func makeMkdirCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_MkdirRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_MkdirRequest, Com_Apple_Containerization_Sandbox_V3_MkdirResponse>\n\n  func makeSysctlCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SysctlRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_SysctlRequest, Com_Apple_Containerization_Sandbox_V3_SysctlResponse>\n\n  func makeSetTimeCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SetTimeRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_SetTimeRequest, Com_Apple_Containerization_Sandbox_V3_SetTimeResponse>\n\n  func makeSetupEmulatorCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest, Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse>\n\n  func makeWriteFileCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, Com_Apple_Containerization_Sandbox_V3_WriteFileResponse>\n\n  func makeCopyCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_CopyRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncServerStreamingCall<Com_Apple_Containerization_Sandbox_V3_CopyRequest, Com_Apple_Containerization_Sandbox_V3_CopyResponse>\n\n  func makeCreateProcessCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest, Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse>\n\n  func makeDeleteProcessCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest, Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse>\n\n  func makeStartProcessCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_StartProcessRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_StartProcessRequest, Com_Apple_Containerization_Sandbox_V3_StartProcessResponse>\n\n  func makeKillProcessCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_KillProcessRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_KillProcessRequest, Com_Apple_Containerization_Sandbox_V3_KillProcessResponse>\n\n  func makeWaitProcessCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest, Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse>\n\n  func makeResizeProcessCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest, Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse>\n\n  func makeCloseProcessStdinCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest, Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse>\n\n  func makeContainerStatisticsCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest, Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse>\n\n  func makeProxyVsockCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest, Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse>\n\n  func makeStopVsockProxyCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest, Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse>\n\n  func makeIpLinkSetCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest, Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse>\n\n  func makeIpAddrAddCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest, Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse>\n\n  func makeIpRouteAddLinkCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest, Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse>\n\n  func makeIpRouteAddDefaultCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest, Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse>\n\n  func makeConfigureDnsCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest, Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse>\n\n  func makeConfigureHostsCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest, Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse>\n\n  func makeSyncCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SyncRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_SyncRequest, Com_Apple_Containerization_Sandbox_V3_SyncResponse>\n\n  func makeKillCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_KillRequest,\n    callOptions: CallOptions?\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_KillRequest, Com_Apple_Containerization_Sandbox_V3_KillResponse>\n}\n\n@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)\nextension Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncClientProtocol {\n  public static var serviceDescriptor: GRPCServiceDescriptor {\n    return Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.serviceDescriptor\n  }\n\n  public var interceptors: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientInterceptorFactoryProtocol? {\n    return nil\n  }\n\n  public func makeMountCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_MountRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_MountRequest, Com_Apple_Containerization_Sandbox_V3_MountResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.mount.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeMountInterceptors() ?? []\n    )\n  }\n\n  public func makeUmountCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_UmountRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_UmountRequest, Com_Apple_Containerization_Sandbox_V3_UmountResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.umount.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeUmountInterceptors() ?? []\n    )\n  }\n\n  public func makeSetenvCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SetenvRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_SetenvRequest, Com_Apple_Containerization_Sandbox_V3_SetenvResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.setenv.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeSetenvInterceptors() ?? []\n    )\n  }\n\n  public func makeGetenvCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_GetenvRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_GetenvRequest, Com_Apple_Containerization_Sandbox_V3_GetenvResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.getenv.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeGetenvInterceptors() ?? []\n    )\n  }\n\n  public func makeMkdirCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_MkdirRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_MkdirRequest, Com_Apple_Containerization_Sandbox_V3_MkdirResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.mkdir.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeMkdirInterceptors() ?? []\n    )\n  }\n\n  public func makeSysctlCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SysctlRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_SysctlRequest, Com_Apple_Containerization_Sandbox_V3_SysctlResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.sysctl.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeSysctlInterceptors() ?? []\n    )\n  }\n\n  public func makeSetTimeCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SetTimeRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_SetTimeRequest, Com_Apple_Containerization_Sandbox_V3_SetTimeResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.setTime.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeSetTimeInterceptors() ?? []\n    )\n  }\n\n  public func makeSetupEmulatorCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest, Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.setupEmulator.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeSetupEmulatorInterceptors() ?? []\n    )\n  }\n\n  public func makeWriteFileCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, Com_Apple_Containerization_Sandbox_V3_WriteFileResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.writeFile.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeWriteFileInterceptors() ?? []\n    )\n  }\n\n  public func makeCopyCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_CopyRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncServerStreamingCall<Com_Apple_Containerization_Sandbox_V3_CopyRequest, Com_Apple_Containerization_Sandbox_V3_CopyResponse> {\n    return self.makeAsyncServerStreamingCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.copy.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeCopyInterceptors() ?? []\n    )\n  }\n\n  public func makeCreateProcessCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest, Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.createProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeCreateProcessInterceptors() ?? []\n    )\n  }\n\n  public func makeDeleteProcessCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest, Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.deleteProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeDeleteProcessInterceptors() ?? []\n    )\n  }\n\n  public func makeStartProcessCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_StartProcessRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_StartProcessRequest, Com_Apple_Containerization_Sandbox_V3_StartProcessResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.startProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeStartProcessInterceptors() ?? []\n    )\n  }\n\n  public func makeKillProcessCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_KillProcessRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_KillProcessRequest, Com_Apple_Containerization_Sandbox_V3_KillProcessResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.killProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeKillProcessInterceptors() ?? []\n    )\n  }\n\n  public func makeWaitProcessCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest, Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.waitProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeWaitProcessInterceptors() ?? []\n    )\n  }\n\n  public func makeResizeProcessCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest, Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.resizeProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeResizeProcessInterceptors() ?? []\n    )\n  }\n\n  public func makeCloseProcessStdinCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest, Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.closeProcessStdin.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeCloseProcessStdinInterceptors() ?? []\n    )\n  }\n\n  public func makeContainerStatisticsCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest, Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.containerStatistics.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeContainerStatisticsInterceptors() ?? []\n    )\n  }\n\n  public func makeProxyVsockCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest, Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.proxyVsock.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeProxyVsockInterceptors() ?? []\n    )\n  }\n\n  public func makeStopVsockProxyCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest, Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.stopVsockProxy.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeStopVsockProxyInterceptors() ?? []\n    )\n  }\n\n  public func makeIpLinkSetCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest, Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipLinkSet.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeIpLinkSetInterceptors() ?? []\n    )\n  }\n\n  public func makeIpAddrAddCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest, Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipAddrAdd.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeIpAddrAddInterceptors() ?? []\n    )\n  }\n\n  public func makeIpRouteAddLinkCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest, Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipRouteAddLink.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeIpRouteAddLinkInterceptors() ?? []\n    )\n  }\n\n  public func makeIpRouteAddDefaultCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest, Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipRouteAddDefault.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeIpRouteAddDefaultInterceptors() ?? []\n    )\n  }\n\n  public func makeConfigureDnsCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest, Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.configureDns.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeConfigureDnsInterceptors() ?? []\n    )\n  }\n\n  public func makeConfigureHostsCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest, Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.configureHosts.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeConfigureHostsInterceptors() ?? []\n    )\n  }\n\n  public func makeSyncCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SyncRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_SyncRequest, Com_Apple_Containerization_Sandbox_V3_SyncResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.sync.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeSyncInterceptors() ?? []\n    )\n  }\n\n  public func makeKillCall(\n    _ request: Com_Apple_Containerization_Sandbox_V3_KillRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_KillRequest, Com_Apple_Containerization_Sandbox_V3_KillResponse> {\n    return self.makeAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.kill.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeKillInterceptors() ?? []\n    )\n  }\n}\n\n@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)\nextension Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncClientProtocol {\n  public func mount(\n    _ request: Com_Apple_Containerization_Sandbox_V3_MountRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_MountResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.mount.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeMountInterceptors() ?? []\n    )\n  }\n\n  public func umount(\n    _ request: Com_Apple_Containerization_Sandbox_V3_UmountRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_UmountResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.umount.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeUmountInterceptors() ?? []\n    )\n  }\n\n  public func setenv(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SetenvRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_SetenvResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.setenv.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeSetenvInterceptors() ?? []\n    )\n  }\n\n  public func getenv(\n    _ request: Com_Apple_Containerization_Sandbox_V3_GetenvRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_GetenvResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.getenv.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeGetenvInterceptors() ?? []\n    )\n  }\n\n  public func mkdir(\n    _ request: Com_Apple_Containerization_Sandbox_V3_MkdirRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_MkdirResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.mkdir.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeMkdirInterceptors() ?? []\n    )\n  }\n\n  public func sysctl(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SysctlRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_SysctlResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.sysctl.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeSysctlInterceptors() ?? []\n    )\n  }\n\n  public func setTime(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SetTimeRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_SetTimeResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.setTime.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeSetTimeInterceptors() ?? []\n    )\n  }\n\n  public func setupEmulator(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.setupEmulator.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeSetupEmulatorInterceptors() ?? []\n    )\n  }\n\n  public func writeFile(\n    _ request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_WriteFileResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.writeFile.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeWriteFileInterceptors() ?? []\n    )\n  }\n\n  public func copy(\n    _ request: Com_Apple_Containerization_Sandbox_V3_CopyRequest,\n    callOptions: CallOptions? = nil\n  ) -> GRPCAsyncResponseStream<Com_Apple_Containerization_Sandbox_V3_CopyResponse> {\n    return self.performAsyncServerStreamingCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.copy.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeCopyInterceptors() ?? []\n    )\n  }\n\n  public func createProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.createProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeCreateProcessInterceptors() ?? []\n    )\n  }\n\n  public func deleteProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.deleteProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeDeleteProcessInterceptors() ?? []\n    )\n  }\n\n  public func startProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_StartProcessRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_StartProcessResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.startProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeStartProcessInterceptors() ?? []\n    )\n  }\n\n  public func killProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_KillProcessRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_KillProcessResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.killProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeKillProcessInterceptors() ?? []\n    )\n  }\n\n  public func waitProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.waitProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeWaitProcessInterceptors() ?? []\n    )\n  }\n\n  public func resizeProcess(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.resizeProcess.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeResizeProcessInterceptors() ?? []\n    )\n  }\n\n  public func closeProcessStdin(\n    _ request: Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.closeProcessStdin.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeCloseProcessStdinInterceptors() ?? []\n    )\n  }\n\n  public func containerStatistics(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.containerStatistics.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeContainerStatisticsInterceptors() ?? []\n    )\n  }\n\n  public func proxyVsock(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.proxyVsock.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeProxyVsockInterceptors() ?? []\n    )\n  }\n\n  public func stopVsockProxy(\n    _ request: Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.stopVsockProxy.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeStopVsockProxyInterceptors() ?? []\n    )\n  }\n\n  public func ipLinkSet(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipLinkSet.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeIpLinkSetInterceptors() ?? []\n    )\n  }\n\n  public func ipAddrAdd(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipAddrAdd.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeIpAddrAddInterceptors() ?? []\n    )\n  }\n\n  public func ipRouteAddLink(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipRouteAddLink.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeIpRouteAddLinkInterceptors() ?? []\n    )\n  }\n\n  public func ipRouteAddDefault(\n    _ request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipRouteAddDefault.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeIpRouteAddDefaultInterceptors() ?? []\n    )\n  }\n\n  public func configureDns(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.configureDns.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeConfigureDnsInterceptors() ?? []\n    )\n  }\n\n  public func configureHosts(\n    _ request: Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.configureHosts.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeConfigureHostsInterceptors() ?? []\n    )\n  }\n\n  public func sync(\n    _ request: Com_Apple_Containerization_Sandbox_V3_SyncRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_SyncResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.sync.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeSyncInterceptors() ?? []\n    )\n  }\n\n  public func kill(\n    _ request: Com_Apple_Containerization_Sandbox_V3_KillRequest,\n    callOptions: CallOptions? = nil\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_KillResponse {\n    return try await self.performAsyncUnaryCall(\n      path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.kill.path,\n      request: request,\n      callOptions: callOptions ?? self.defaultCallOptions,\n      interceptors: self.interceptors?.makeKillInterceptors() ?? []\n    )\n  }\n}\n\n@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)\npublic struct Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncClient: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncClientProtocol {\n  public var channel: GRPCChannel\n  public var defaultCallOptions: CallOptions\n  public var interceptors: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientInterceptorFactoryProtocol?\n\n  public init(\n    channel: GRPCChannel,\n    defaultCallOptions: CallOptions = CallOptions(),\n    interceptors: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientInterceptorFactoryProtocol? = nil\n  ) {\n    self.channel = channel\n    self.defaultCallOptions = defaultCallOptions\n    self.interceptors = interceptors\n  }\n}\n\npublic protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextClientInterceptorFactoryProtocol: Sendable {\n\n  /// - Returns: Interceptors to use when invoking 'mount'.\n  func makeMountInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_MountRequest, Com_Apple_Containerization_Sandbox_V3_MountResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'umount'.\n  func makeUmountInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_UmountRequest, Com_Apple_Containerization_Sandbox_V3_UmountResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'setenv'.\n  func makeSetenvInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_SetenvRequest, Com_Apple_Containerization_Sandbox_V3_SetenvResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'getenv'.\n  func makeGetenvInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_GetenvRequest, Com_Apple_Containerization_Sandbox_V3_GetenvResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'mkdir'.\n  func makeMkdirInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_MkdirRequest, Com_Apple_Containerization_Sandbox_V3_MkdirResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'sysctl'.\n  func makeSysctlInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_SysctlRequest, Com_Apple_Containerization_Sandbox_V3_SysctlResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'setTime'.\n  func makeSetTimeInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_SetTimeRequest, Com_Apple_Containerization_Sandbox_V3_SetTimeResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'setupEmulator'.\n  func makeSetupEmulatorInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest, Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'writeFile'.\n  func makeWriteFileInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, Com_Apple_Containerization_Sandbox_V3_WriteFileResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'copy'.\n  func makeCopyInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_CopyRequest, Com_Apple_Containerization_Sandbox_V3_CopyResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'createProcess'.\n  func makeCreateProcessInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest, Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'deleteProcess'.\n  func makeDeleteProcessInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest, Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'startProcess'.\n  func makeStartProcessInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_StartProcessRequest, Com_Apple_Containerization_Sandbox_V3_StartProcessResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'killProcess'.\n  func makeKillProcessInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_KillProcessRequest, Com_Apple_Containerization_Sandbox_V3_KillProcessResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'waitProcess'.\n  func makeWaitProcessInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest, Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'resizeProcess'.\n  func makeResizeProcessInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest, Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'closeProcessStdin'.\n  func makeCloseProcessStdinInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest, Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'containerStatistics'.\n  func makeContainerStatisticsInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest, Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'proxyVsock'.\n  func makeProxyVsockInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest, Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'stopVsockProxy'.\n  func makeStopVsockProxyInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest, Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'ipLinkSet'.\n  func makeIpLinkSetInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest, Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'ipAddrAdd'.\n  func makeIpAddrAddInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest, Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'ipRouteAddLink'.\n  func makeIpRouteAddLinkInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest, Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'ipRouteAddDefault'.\n  func makeIpRouteAddDefaultInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest, Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'configureDns'.\n  func makeConfigureDnsInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest, Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'configureHosts'.\n  func makeConfigureHostsInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest, Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'sync'.\n  func makeSyncInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_SyncRequest, Com_Apple_Containerization_Sandbox_V3_SyncResponse>]\n\n  /// - Returns: Interceptors to use when invoking 'kill'.\n  func makeKillInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_KillRequest, Com_Apple_Containerization_Sandbox_V3_KillResponse>]\n}\n\npublic enum Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata {\n  public static let serviceDescriptor = GRPCServiceDescriptor(\n    name: \"SandboxContext\",\n    fullName: \"com.apple.containerization.sandbox.v3.SandboxContext\",\n    methods: [\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.mount,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.umount,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.setenv,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.getenv,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.mkdir,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.sysctl,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.setTime,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.setupEmulator,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.writeFile,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.copy,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.createProcess,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.deleteProcess,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.startProcess,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.killProcess,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.waitProcess,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.resizeProcess,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.closeProcessStdin,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.containerStatistics,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.proxyVsock,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.stopVsockProxy,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipLinkSet,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipAddrAdd,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipRouteAddLink,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.ipRouteAddDefault,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.configureDns,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.configureHosts,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.sync,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.kill,\n    ]\n  )\n\n  public enum Methods {\n    public static let mount = GRPCMethodDescriptor(\n      name: \"Mount\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Mount\",\n      type: GRPCCallType.unary\n    )\n\n    public static let umount = GRPCMethodDescriptor(\n      name: \"Umount\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Umount\",\n      type: GRPCCallType.unary\n    )\n\n    public static let setenv = GRPCMethodDescriptor(\n      name: \"Setenv\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Setenv\",\n      type: GRPCCallType.unary\n    )\n\n    public static let getenv = GRPCMethodDescriptor(\n      name: \"Getenv\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Getenv\",\n      type: GRPCCallType.unary\n    )\n\n    public static let mkdir = GRPCMethodDescriptor(\n      name: \"Mkdir\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Mkdir\",\n      type: GRPCCallType.unary\n    )\n\n    public static let sysctl = GRPCMethodDescriptor(\n      name: \"Sysctl\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Sysctl\",\n      type: GRPCCallType.unary\n    )\n\n    public static let setTime = GRPCMethodDescriptor(\n      name: \"SetTime\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/SetTime\",\n      type: GRPCCallType.unary\n    )\n\n    public static let setupEmulator = GRPCMethodDescriptor(\n      name: \"SetupEmulator\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/SetupEmulator\",\n      type: GRPCCallType.unary\n    )\n\n    public static let writeFile = GRPCMethodDescriptor(\n      name: \"WriteFile\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/WriteFile\",\n      type: GRPCCallType.unary\n    )\n\n    public static let copy = GRPCMethodDescriptor(\n      name: \"Copy\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Copy\",\n      type: GRPCCallType.serverStreaming\n    )\n\n    public static let createProcess = GRPCMethodDescriptor(\n      name: \"CreateProcess\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/CreateProcess\",\n      type: GRPCCallType.unary\n    )\n\n    public static let deleteProcess = GRPCMethodDescriptor(\n      name: \"DeleteProcess\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/DeleteProcess\",\n      type: GRPCCallType.unary\n    )\n\n    public static let startProcess = GRPCMethodDescriptor(\n      name: \"StartProcess\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/StartProcess\",\n      type: GRPCCallType.unary\n    )\n\n    public static let killProcess = GRPCMethodDescriptor(\n      name: \"KillProcess\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/KillProcess\",\n      type: GRPCCallType.unary\n    )\n\n    public static let waitProcess = GRPCMethodDescriptor(\n      name: \"WaitProcess\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/WaitProcess\",\n      type: GRPCCallType.unary\n    )\n\n    public static let resizeProcess = GRPCMethodDescriptor(\n      name: \"ResizeProcess\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/ResizeProcess\",\n      type: GRPCCallType.unary\n    )\n\n    public static let closeProcessStdin = GRPCMethodDescriptor(\n      name: \"CloseProcessStdin\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/CloseProcessStdin\",\n      type: GRPCCallType.unary\n    )\n\n    public static let containerStatistics = GRPCMethodDescriptor(\n      name: \"ContainerStatistics\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/ContainerStatistics\",\n      type: GRPCCallType.unary\n    )\n\n    public static let proxyVsock = GRPCMethodDescriptor(\n      name: \"ProxyVsock\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/ProxyVsock\",\n      type: GRPCCallType.unary\n    )\n\n    public static let stopVsockProxy = GRPCMethodDescriptor(\n      name: \"StopVsockProxy\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/StopVsockProxy\",\n      type: GRPCCallType.unary\n    )\n\n    public static let ipLinkSet = GRPCMethodDescriptor(\n      name: \"IpLinkSet\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/IpLinkSet\",\n      type: GRPCCallType.unary\n    )\n\n    public static let ipAddrAdd = GRPCMethodDescriptor(\n      name: \"IpAddrAdd\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/IpAddrAdd\",\n      type: GRPCCallType.unary\n    )\n\n    public static let ipRouteAddLink = GRPCMethodDescriptor(\n      name: \"IpRouteAddLink\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/IpRouteAddLink\",\n      type: GRPCCallType.unary\n    )\n\n    public static let ipRouteAddDefault = GRPCMethodDescriptor(\n      name: \"IpRouteAddDefault\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/IpRouteAddDefault\",\n      type: GRPCCallType.unary\n    )\n\n    public static let configureDns = GRPCMethodDescriptor(\n      name: \"ConfigureDns\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/ConfigureDns\",\n      type: GRPCCallType.unary\n    )\n\n    public static let configureHosts = GRPCMethodDescriptor(\n      name: \"ConfigureHosts\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/ConfigureHosts\",\n      type: GRPCCallType.unary\n    )\n\n    public static let sync = GRPCMethodDescriptor(\n      name: \"Sync\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Sync\",\n      type: GRPCCallType.unary\n    )\n\n    public static let kill = GRPCMethodDescriptor(\n      name: \"Kill\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Kill\",\n      type: GRPCCallType.unary\n    )\n  }\n}\n\n/// Context for interacting with a container's runtime environment.\n///\n/// To build a server, implement a class that conforms to this protocol.\npublic protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextProvider: CallHandlerProvider {\n  var interceptors: Com_Apple_Containerization_Sandbox_V3_SandboxContextServerInterceptorFactoryProtocol? { get }\n\n  /// Mount a filesystem.\n  func mount(request: Com_Apple_Containerization_Sandbox_V3_MountRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_MountResponse>\n\n  /// Unmount a filesystem.\n  func umount(request: Com_Apple_Containerization_Sandbox_V3_UmountRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_UmountResponse>\n\n  /// Set an environment variable on the init process.\n  func setenv(request: Com_Apple_Containerization_Sandbox_V3_SetenvRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_SetenvResponse>\n\n  /// Get an environment variable from the init process.\n  func getenv(request: Com_Apple_Containerization_Sandbox_V3_GetenvRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_GetenvResponse>\n\n  /// Create a new directory inside the sandbox.\n  func mkdir(request: Com_Apple_Containerization_Sandbox_V3_MkdirRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_MkdirResponse>\n\n  /// Set sysctls in the context of the sandbox.\n  func sysctl(request: Com_Apple_Containerization_Sandbox_V3_SysctlRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_SysctlResponse>\n\n  /// Set time in the guest.\n  func setTime(request: Com_Apple_Containerization_Sandbox_V3_SetTimeRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_SetTimeResponse>\n\n  /// Set up an emulator in the guest for a specific binary format.\n  func setupEmulator(request: Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse>\n\n  /// Write data to an existing or new file.\n  func writeFile(request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_WriteFileResponse>\n\n  /// Copy a file or directory between the host and guest.\n  /// Data transfer happens over a dedicated vsock connection;\n  /// the gRPC stream is used only for control/metadata.\n  func copy(request: Com_Apple_Containerization_Sandbox_V3_CopyRequest, context: StreamingResponseCallContext<Com_Apple_Containerization_Sandbox_V3_CopyResponse>) -> EventLoopFuture<GRPCStatus>\n\n  /// Create a new process inside the container.\n  func createProcess(request: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse>\n\n  /// Delete an existing process inside the container.\n  func deleteProcess(request: Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse>\n\n  /// Start the provided process.\n  func startProcess(request: Com_Apple_Containerization_Sandbox_V3_StartProcessRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_StartProcessResponse>\n\n  /// Send a signal to the provided process.\n  func killProcess(request: Com_Apple_Containerization_Sandbox_V3_KillProcessRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_KillProcessResponse>\n\n  /// Wait for a process to exit and return the exit code.\n  func waitProcess(request: Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse>\n\n  /// Resize the tty of a given process. This will error if the process does\n  /// not have a pty allocated.\n  func resizeProcess(request: Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse>\n\n  /// Close IO for a given process.\n  func closeProcessStdin(request: Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse>\n\n  /// Get statistics for containers.\n  func containerStatistics(request: Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse>\n\n  /// Proxy a vsock port to a unix domain socket in the guest, or vice versa.\n  func proxyVsock(request: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse>\n\n  /// Stop a vsock proxy to a unix domain socket.\n  func stopVsockProxy(request: Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse>\n\n  /// Set the link state of a network interface.\n  func ipLinkSet(request: Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse>\n\n  /// Add an IPv4 address to a network interface.\n  func ipAddrAdd(request: Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse>\n\n  /// Add an IP route for a network interface.\n  func ipRouteAddLink(request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse>\n\n  /// Add an IP route for a network interface.\n  func ipRouteAddDefault(request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse>\n\n  /// Configure DNS resolver.\n  func configureDns(request: Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse>\n\n  /// Configure /etc/hosts.\n  func configureHosts(request: Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse>\n\n  /// Perform the sync syscall.\n  func sync(request: Com_Apple_Containerization_Sandbox_V3_SyncRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_SyncResponse>\n\n  /// Send a signal to a process via the PID.\n  func kill(request: Com_Apple_Containerization_Sandbox_V3_KillRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_KillResponse>\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_SandboxContextProvider {\n  public var serviceName: Substring {\n    return Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.serviceDescriptor.fullName[...]\n  }\n\n  /// Determines, calls and returns the appropriate request handler, depending on the request's method.\n  /// Returns nil for methods not handled by this service.\n  public func handle(\n    method name: Substring,\n    context: CallHandlerContext\n  ) -> GRPCServerHandlerProtocol? {\n    switch name {\n    case \"Mount\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_MountRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_MountResponse>(),\n        interceptors: self.interceptors?.makeMountInterceptors() ?? [],\n        userFunction: self.mount(request:context:)\n      )\n\n    case \"Umount\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_UmountRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_UmountResponse>(),\n        interceptors: self.interceptors?.makeUmountInterceptors() ?? [],\n        userFunction: self.umount(request:context:)\n      )\n\n    case \"Setenv\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_SetenvRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_SetenvResponse>(),\n        interceptors: self.interceptors?.makeSetenvInterceptors() ?? [],\n        userFunction: self.setenv(request:context:)\n      )\n\n    case \"Getenv\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_GetenvRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_GetenvResponse>(),\n        interceptors: self.interceptors?.makeGetenvInterceptors() ?? [],\n        userFunction: self.getenv(request:context:)\n      )\n\n    case \"Mkdir\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_MkdirRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_MkdirResponse>(),\n        interceptors: self.interceptors?.makeMkdirInterceptors() ?? [],\n        userFunction: self.mkdir(request:context:)\n      )\n\n    case \"Sysctl\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_SysctlRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_SysctlResponse>(),\n        interceptors: self.interceptors?.makeSysctlInterceptors() ?? [],\n        userFunction: self.sysctl(request:context:)\n      )\n\n    case \"SetTime\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_SetTimeRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_SetTimeResponse>(),\n        interceptors: self.interceptors?.makeSetTimeInterceptors() ?? [],\n        userFunction: self.setTime(request:context:)\n      )\n\n    case \"SetupEmulator\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse>(),\n        interceptors: self.interceptors?.makeSetupEmulatorInterceptors() ?? [],\n        userFunction: self.setupEmulator(request:context:)\n      )\n\n    case \"WriteFile\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_WriteFileResponse>(),\n        interceptors: self.interceptors?.makeWriteFileInterceptors() ?? [],\n        userFunction: self.writeFile(request:context:)\n      )\n\n    case \"Copy\":\n      return ServerStreamingServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_CopyRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_CopyResponse>(),\n        interceptors: self.interceptors?.makeCopyInterceptors() ?? [],\n        userFunction: self.copy(request:context:)\n      )\n\n    case \"CreateProcess\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse>(),\n        interceptors: self.interceptors?.makeCreateProcessInterceptors() ?? [],\n        userFunction: self.createProcess(request:context:)\n      )\n\n    case \"DeleteProcess\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse>(),\n        interceptors: self.interceptors?.makeDeleteProcessInterceptors() ?? [],\n        userFunction: self.deleteProcess(request:context:)\n      )\n\n    case \"StartProcess\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_StartProcessRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_StartProcessResponse>(),\n        interceptors: self.interceptors?.makeStartProcessInterceptors() ?? [],\n        userFunction: self.startProcess(request:context:)\n      )\n\n    case \"KillProcess\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_KillProcessRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_KillProcessResponse>(),\n        interceptors: self.interceptors?.makeKillProcessInterceptors() ?? [],\n        userFunction: self.killProcess(request:context:)\n      )\n\n    case \"WaitProcess\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse>(),\n        interceptors: self.interceptors?.makeWaitProcessInterceptors() ?? [],\n        userFunction: self.waitProcess(request:context:)\n      )\n\n    case \"ResizeProcess\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse>(),\n        interceptors: self.interceptors?.makeResizeProcessInterceptors() ?? [],\n        userFunction: self.resizeProcess(request:context:)\n      )\n\n    case \"CloseProcessStdin\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse>(),\n        interceptors: self.interceptors?.makeCloseProcessStdinInterceptors() ?? [],\n        userFunction: self.closeProcessStdin(request:context:)\n      )\n\n    case \"ContainerStatistics\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse>(),\n        interceptors: self.interceptors?.makeContainerStatisticsInterceptors() ?? [],\n        userFunction: self.containerStatistics(request:context:)\n      )\n\n    case \"ProxyVsock\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse>(),\n        interceptors: self.interceptors?.makeProxyVsockInterceptors() ?? [],\n        userFunction: self.proxyVsock(request:context:)\n      )\n\n    case \"StopVsockProxy\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse>(),\n        interceptors: self.interceptors?.makeStopVsockProxyInterceptors() ?? [],\n        userFunction: self.stopVsockProxy(request:context:)\n      )\n\n    case \"IpLinkSet\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse>(),\n        interceptors: self.interceptors?.makeIpLinkSetInterceptors() ?? [],\n        userFunction: self.ipLinkSet(request:context:)\n      )\n\n    case \"IpAddrAdd\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse>(),\n        interceptors: self.interceptors?.makeIpAddrAddInterceptors() ?? [],\n        userFunction: self.ipAddrAdd(request:context:)\n      )\n\n    case \"IpRouteAddLink\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse>(),\n        interceptors: self.interceptors?.makeIpRouteAddLinkInterceptors() ?? [],\n        userFunction: self.ipRouteAddLink(request:context:)\n      )\n\n    case \"IpRouteAddDefault\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse>(),\n        interceptors: self.interceptors?.makeIpRouteAddDefaultInterceptors() ?? [],\n        userFunction: self.ipRouteAddDefault(request:context:)\n      )\n\n    case \"ConfigureDns\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse>(),\n        interceptors: self.interceptors?.makeConfigureDnsInterceptors() ?? [],\n        userFunction: self.configureDns(request:context:)\n      )\n\n    case \"ConfigureHosts\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse>(),\n        interceptors: self.interceptors?.makeConfigureHostsInterceptors() ?? [],\n        userFunction: self.configureHosts(request:context:)\n      )\n\n    case \"Sync\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_SyncRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_SyncResponse>(),\n        interceptors: self.interceptors?.makeSyncInterceptors() ?? [],\n        userFunction: self.sync(request:context:)\n      )\n\n    case \"Kill\":\n      return UnaryServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_KillRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_KillResponse>(),\n        interceptors: self.interceptors?.makeKillInterceptors() ?? [],\n        userFunction: self.kill(request:context:)\n      )\n\n    default:\n      return nil\n    }\n  }\n}\n\n/// Context for interacting with a container's runtime environment.\n///\n/// To implement a server, implement an object which conforms to this protocol.\n@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)\npublic protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvider: CallHandlerProvider, Sendable {\n  static var serviceDescriptor: GRPCServiceDescriptor { get }\n  var interceptors: Com_Apple_Containerization_Sandbox_V3_SandboxContextServerInterceptorFactoryProtocol? { get }\n\n  /// Mount a filesystem.\n  func mount(\n    request: Com_Apple_Containerization_Sandbox_V3_MountRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_MountResponse\n\n  /// Unmount a filesystem.\n  func umount(\n    request: Com_Apple_Containerization_Sandbox_V3_UmountRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_UmountResponse\n\n  /// Set an environment variable on the init process.\n  func setenv(\n    request: Com_Apple_Containerization_Sandbox_V3_SetenvRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_SetenvResponse\n\n  /// Get an environment variable from the init process.\n  func getenv(\n    request: Com_Apple_Containerization_Sandbox_V3_GetenvRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_GetenvResponse\n\n  /// Create a new directory inside the sandbox.\n  func mkdir(\n    request: Com_Apple_Containerization_Sandbox_V3_MkdirRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_MkdirResponse\n\n  /// Set sysctls in the context of the sandbox.\n  func sysctl(\n    request: Com_Apple_Containerization_Sandbox_V3_SysctlRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_SysctlResponse\n\n  /// Set time in the guest.\n  func setTime(\n    request: Com_Apple_Containerization_Sandbox_V3_SetTimeRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_SetTimeResponse\n\n  /// Set up an emulator in the guest for a specific binary format.\n  func setupEmulator(\n    request: Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse\n\n  /// Write data to an existing or new file.\n  func writeFile(\n    request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_WriteFileResponse\n\n  /// Copy a file or directory between the host and guest.\n  /// Data transfer happens over a dedicated vsock connection;\n  /// the gRPC stream is used only for control/metadata.\n  func copy(\n    request: Com_Apple_Containerization_Sandbox_V3_CopyRequest,\n    responseStream: GRPCAsyncResponseStreamWriter<Com_Apple_Containerization_Sandbox_V3_CopyResponse>,\n    context: GRPCAsyncServerCallContext\n  ) async throws\n\n  /// Create a new process inside the container.\n  func createProcess(\n    request: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse\n\n  /// Delete an existing process inside the container.\n  func deleteProcess(\n    request: Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse\n\n  /// Start the provided process.\n  func startProcess(\n    request: Com_Apple_Containerization_Sandbox_V3_StartProcessRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_StartProcessResponse\n\n  /// Send a signal to the provided process.\n  func killProcess(\n    request: Com_Apple_Containerization_Sandbox_V3_KillProcessRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_KillProcessResponse\n\n  /// Wait for a process to exit and return the exit code.\n  func waitProcess(\n    request: Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse\n\n  /// Resize the tty of a given process. This will error if the process does\n  /// not have a pty allocated.\n  func resizeProcess(\n    request: Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse\n\n  /// Close IO for a given process.\n  func closeProcessStdin(\n    request: Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse\n\n  /// Get statistics for containers.\n  func containerStatistics(\n    request: Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse\n\n  /// Proxy a vsock port to a unix domain socket in the guest, or vice versa.\n  func proxyVsock(\n    request: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse\n\n  /// Stop a vsock proxy to a unix domain socket.\n  func stopVsockProxy(\n    request: Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse\n\n  /// Set the link state of a network interface.\n  func ipLinkSet(\n    request: Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse\n\n  /// Add an IPv4 address to a network interface.\n  func ipAddrAdd(\n    request: Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse\n\n  /// Add an IP route for a network interface.\n  func ipRouteAddLink(\n    request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse\n\n  /// Add an IP route for a network interface.\n  func ipRouteAddDefault(\n    request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse\n\n  /// Configure DNS resolver.\n  func configureDns(\n    request: Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse\n\n  /// Configure /etc/hosts.\n  func configureHosts(\n    request: Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse\n\n  /// Perform the sync syscall.\n  func sync(\n    request: Com_Apple_Containerization_Sandbox_V3_SyncRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_SyncResponse\n\n  /// Send a signal to a process via the PID.\n  func kill(\n    request: Com_Apple_Containerization_Sandbox_V3_KillRequest,\n    context: GRPCAsyncServerCallContext\n  ) async throws -> Com_Apple_Containerization_Sandbox_V3_KillResponse\n}\n\n@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)\nextension Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvider {\n  public static var serviceDescriptor: GRPCServiceDescriptor {\n    return Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.serviceDescriptor\n  }\n\n  public var serviceName: Substring {\n    return Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.serviceDescriptor.fullName[...]\n  }\n\n  public var interceptors: Com_Apple_Containerization_Sandbox_V3_SandboxContextServerInterceptorFactoryProtocol? {\n    return nil\n  }\n\n  public func handle(\n    method name: Substring,\n    context: CallHandlerContext\n  ) -> GRPCServerHandlerProtocol? {\n    switch name {\n    case \"Mount\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_MountRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_MountResponse>(),\n        interceptors: self.interceptors?.makeMountInterceptors() ?? [],\n        wrapping: { try await self.mount(request: $0, context: $1) }\n      )\n\n    case \"Umount\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_UmountRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_UmountResponse>(),\n        interceptors: self.interceptors?.makeUmountInterceptors() ?? [],\n        wrapping: { try await self.umount(request: $0, context: $1) }\n      )\n\n    case \"Setenv\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_SetenvRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_SetenvResponse>(),\n        interceptors: self.interceptors?.makeSetenvInterceptors() ?? [],\n        wrapping: { try await self.setenv(request: $0, context: $1) }\n      )\n\n    case \"Getenv\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_GetenvRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_GetenvResponse>(),\n        interceptors: self.interceptors?.makeGetenvInterceptors() ?? [],\n        wrapping: { try await self.getenv(request: $0, context: $1) }\n      )\n\n    case \"Mkdir\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_MkdirRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_MkdirResponse>(),\n        interceptors: self.interceptors?.makeMkdirInterceptors() ?? [],\n        wrapping: { try await self.mkdir(request: $0, context: $1) }\n      )\n\n    case \"Sysctl\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_SysctlRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_SysctlResponse>(),\n        interceptors: self.interceptors?.makeSysctlInterceptors() ?? [],\n        wrapping: { try await self.sysctl(request: $0, context: $1) }\n      )\n\n    case \"SetTime\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_SetTimeRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_SetTimeResponse>(),\n        interceptors: self.interceptors?.makeSetTimeInterceptors() ?? [],\n        wrapping: { try await self.setTime(request: $0, context: $1) }\n      )\n\n    case \"SetupEmulator\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse>(),\n        interceptors: self.interceptors?.makeSetupEmulatorInterceptors() ?? [],\n        wrapping: { try await self.setupEmulator(request: $0, context: $1) }\n      )\n\n    case \"WriteFile\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_WriteFileResponse>(),\n        interceptors: self.interceptors?.makeWriteFileInterceptors() ?? [],\n        wrapping: { try await self.writeFile(request: $0, context: $1) }\n      )\n\n    case \"Copy\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_CopyRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_CopyResponse>(),\n        interceptors: self.interceptors?.makeCopyInterceptors() ?? [],\n        wrapping: { try await self.copy(request: $0, responseStream: $1, context: $2) }\n      )\n\n    case \"CreateProcess\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse>(),\n        interceptors: self.interceptors?.makeCreateProcessInterceptors() ?? [],\n        wrapping: { try await self.createProcess(request: $0, context: $1) }\n      )\n\n    case \"DeleteProcess\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse>(),\n        interceptors: self.interceptors?.makeDeleteProcessInterceptors() ?? [],\n        wrapping: { try await self.deleteProcess(request: $0, context: $1) }\n      )\n\n    case \"StartProcess\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_StartProcessRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_StartProcessResponse>(),\n        interceptors: self.interceptors?.makeStartProcessInterceptors() ?? [],\n        wrapping: { try await self.startProcess(request: $0, context: $1) }\n      )\n\n    case \"KillProcess\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_KillProcessRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_KillProcessResponse>(),\n        interceptors: self.interceptors?.makeKillProcessInterceptors() ?? [],\n        wrapping: { try await self.killProcess(request: $0, context: $1) }\n      )\n\n    case \"WaitProcess\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse>(),\n        interceptors: self.interceptors?.makeWaitProcessInterceptors() ?? [],\n        wrapping: { try await self.waitProcess(request: $0, context: $1) }\n      )\n\n    case \"ResizeProcess\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse>(),\n        interceptors: self.interceptors?.makeResizeProcessInterceptors() ?? [],\n        wrapping: { try await self.resizeProcess(request: $0, context: $1) }\n      )\n\n    case \"CloseProcessStdin\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse>(),\n        interceptors: self.interceptors?.makeCloseProcessStdinInterceptors() ?? [],\n        wrapping: { try await self.closeProcessStdin(request: $0, context: $1) }\n      )\n\n    case \"ContainerStatistics\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse>(),\n        interceptors: self.interceptors?.makeContainerStatisticsInterceptors() ?? [],\n        wrapping: { try await self.containerStatistics(request: $0, context: $1) }\n      )\n\n    case \"ProxyVsock\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse>(),\n        interceptors: self.interceptors?.makeProxyVsockInterceptors() ?? [],\n        wrapping: { try await self.proxyVsock(request: $0, context: $1) }\n      )\n\n    case \"StopVsockProxy\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse>(),\n        interceptors: self.interceptors?.makeStopVsockProxyInterceptors() ?? [],\n        wrapping: { try await self.stopVsockProxy(request: $0, context: $1) }\n      )\n\n    case \"IpLinkSet\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse>(),\n        interceptors: self.interceptors?.makeIpLinkSetInterceptors() ?? [],\n        wrapping: { try await self.ipLinkSet(request: $0, context: $1) }\n      )\n\n    case \"IpAddrAdd\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse>(),\n        interceptors: self.interceptors?.makeIpAddrAddInterceptors() ?? [],\n        wrapping: { try await self.ipAddrAdd(request: $0, context: $1) }\n      )\n\n    case \"IpRouteAddLink\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse>(),\n        interceptors: self.interceptors?.makeIpRouteAddLinkInterceptors() ?? [],\n        wrapping: { try await self.ipRouteAddLink(request: $0, context: $1) }\n      )\n\n    case \"IpRouteAddDefault\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse>(),\n        interceptors: self.interceptors?.makeIpRouteAddDefaultInterceptors() ?? [],\n        wrapping: { try await self.ipRouteAddDefault(request: $0, context: $1) }\n      )\n\n    case \"ConfigureDns\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse>(),\n        interceptors: self.interceptors?.makeConfigureDnsInterceptors() ?? [],\n        wrapping: { try await self.configureDns(request: $0, context: $1) }\n      )\n\n    case \"ConfigureHosts\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse>(),\n        interceptors: self.interceptors?.makeConfigureHostsInterceptors() ?? [],\n        wrapping: { try await self.configureHosts(request: $0, context: $1) }\n      )\n\n    case \"Sync\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_SyncRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_SyncResponse>(),\n        interceptors: self.interceptors?.makeSyncInterceptors() ?? [],\n        wrapping: { try await self.sync(request: $0, context: $1) }\n      )\n\n    case \"Kill\":\n      return GRPCAsyncServerHandler(\n        context: context,\n        requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_KillRequest>(),\n        responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_KillResponse>(),\n        interceptors: self.interceptors?.makeKillInterceptors() ?? [],\n        wrapping: { try await self.kill(request: $0, context: $1) }\n      )\n\n    default:\n      return nil\n    }\n  }\n}\n\npublic protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextServerInterceptorFactoryProtocol: Sendable {\n\n  /// - Returns: Interceptors to use when handling 'mount'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeMountInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_MountRequest, Com_Apple_Containerization_Sandbox_V3_MountResponse>]\n\n  /// - Returns: Interceptors to use when handling 'umount'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeUmountInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_UmountRequest, Com_Apple_Containerization_Sandbox_V3_UmountResponse>]\n\n  /// - Returns: Interceptors to use when handling 'setenv'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeSetenvInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_SetenvRequest, Com_Apple_Containerization_Sandbox_V3_SetenvResponse>]\n\n  /// - Returns: Interceptors to use when handling 'getenv'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeGetenvInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_GetenvRequest, Com_Apple_Containerization_Sandbox_V3_GetenvResponse>]\n\n  /// - Returns: Interceptors to use when handling 'mkdir'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeMkdirInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_MkdirRequest, Com_Apple_Containerization_Sandbox_V3_MkdirResponse>]\n\n  /// - Returns: Interceptors to use when handling 'sysctl'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeSysctlInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_SysctlRequest, Com_Apple_Containerization_Sandbox_V3_SysctlResponse>]\n\n  /// - Returns: Interceptors to use when handling 'setTime'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeSetTimeInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_SetTimeRequest, Com_Apple_Containerization_Sandbox_V3_SetTimeResponse>]\n\n  /// - Returns: Interceptors to use when handling 'setupEmulator'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeSetupEmulatorInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest, Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse>]\n\n  /// - Returns: Interceptors to use when handling 'writeFile'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeWriteFileInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, Com_Apple_Containerization_Sandbox_V3_WriteFileResponse>]\n\n  /// - Returns: Interceptors to use when handling 'copy'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeCopyInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_CopyRequest, Com_Apple_Containerization_Sandbox_V3_CopyResponse>]\n\n  /// - Returns: Interceptors to use when handling 'createProcess'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeCreateProcessInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest, Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse>]\n\n  /// - Returns: Interceptors to use when handling 'deleteProcess'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeDeleteProcessInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest, Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse>]\n\n  /// - Returns: Interceptors to use when handling 'startProcess'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeStartProcessInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_StartProcessRequest, Com_Apple_Containerization_Sandbox_V3_StartProcessResponse>]\n\n  /// - Returns: Interceptors to use when handling 'killProcess'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeKillProcessInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_KillProcessRequest, Com_Apple_Containerization_Sandbox_V3_KillProcessResponse>]\n\n  /// - Returns: Interceptors to use when handling 'waitProcess'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeWaitProcessInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest, Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse>]\n\n  /// - Returns: Interceptors to use when handling 'resizeProcess'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeResizeProcessInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest, Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse>]\n\n  /// - Returns: Interceptors to use when handling 'closeProcessStdin'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeCloseProcessStdinInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest, Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse>]\n\n  /// - Returns: Interceptors to use when handling 'containerStatistics'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeContainerStatisticsInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest, Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse>]\n\n  /// - Returns: Interceptors to use when handling 'proxyVsock'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeProxyVsockInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest, Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse>]\n\n  /// - Returns: Interceptors to use when handling 'stopVsockProxy'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeStopVsockProxyInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest, Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse>]\n\n  /// - Returns: Interceptors to use when handling 'ipLinkSet'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeIpLinkSetInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest, Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse>]\n\n  /// - Returns: Interceptors to use when handling 'ipAddrAdd'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeIpAddrAddInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest, Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse>]\n\n  /// - Returns: Interceptors to use when handling 'ipRouteAddLink'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeIpRouteAddLinkInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest, Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse>]\n\n  /// - Returns: Interceptors to use when handling 'ipRouteAddDefault'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeIpRouteAddDefaultInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest, Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse>]\n\n  /// - Returns: Interceptors to use when handling 'configureDns'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeConfigureDnsInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest, Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse>]\n\n  /// - Returns: Interceptors to use when handling 'configureHosts'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeConfigureHostsInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest, Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse>]\n\n  /// - Returns: Interceptors to use when handling 'sync'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeSyncInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_SyncRequest, Com_Apple_Containerization_Sandbox_V3_SyncResponse>]\n\n  /// - Returns: Interceptors to use when handling 'kill'.\n  ///   Defaults to calling `self.makeInterceptors()`.\n  func makeKillInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_KillRequest, Com_Apple_Containerization_Sandbox_V3_KillResponse>]\n}\n\npublic enum Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata {\n  public static let serviceDescriptor = GRPCServiceDescriptor(\n    name: \"SandboxContext\",\n    fullName: \"com.apple.containerization.sandbox.v3.SandboxContext\",\n    methods: [\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.mount,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.umount,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.setenv,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.getenv,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.mkdir,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.sysctl,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.setTime,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.setupEmulator,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.writeFile,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.copy,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.createProcess,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.deleteProcess,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.startProcess,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.killProcess,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.waitProcess,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.resizeProcess,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.closeProcessStdin,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.containerStatistics,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.proxyVsock,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.stopVsockProxy,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.ipLinkSet,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.ipAddrAdd,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.ipRouteAddLink,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.ipRouteAddDefault,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.configureDns,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.configureHosts,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.sync,\n      Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.kill,\n    ]\n  )\n\n  public enum Methods {\n    public static let mount = GRPCMethodDescriptor(\n      name: \"Mount\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Mount\",\n      type: GRPCCallType.unary\n    )\n\n    public static let umount = GRPCMethodDescriptor(\n      name: \"Umount\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Umount\",\n      type: GRPCCallType.unary\n    )\n\n    public static let setenv = GRPCMethodDescriptor(\n      name: \"Setenv\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Setenv\",\n      type: GRPCCallType.unary\n    )\n\n    public static let getenv = GRPCMethodDescriptor(\n      name: \"Getenv\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Getenv\",\n      type: GRPCCallType.unary\n    )\n\n    public static let mkdir = GRPCMethodDescriptor(\n      name: \"Mkdir\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Mkdir\",\n      type: GRPCCallType.unary\n    )\n\n    public static let sysctl = GRPCMethodDescriptor(\n      name: \"Sysctl\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Sysctl\",\n      type: GRPCCallType.unary\n    )\n\n    public static let setTime = GRPCMethodDescriptor(\n      name: \"SetTime\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/SetTime\",\n      type: GRPCCallType.unary\n    )\n\n    public static let setupEmulator = GRPCMethodDescriptor(\n      name: \"SetupEmulator\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/SetupEmulator\",\n      type: GRPCCallType.unary\n    )\n\n    public static let writeFile = GRPCMethodDescriptor(\n      name: \"WriteFile\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/WriteFile\",\n      type: GRPCCallType.unary\n    )\n\n    public static let copy = GRPCMethodDescriptor(\n      name: \"Copy\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Copy\",\n      type: GRPCCallType.serverStreaming\n    )\n\n    public static let createProcess = GRPCMethodDescriptor(\n      name: \"CreateProcess\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/CreateProcess\",\n      type: GRPCCallType.unary\n    )\n\n    public static let deleteProcess = GRPCMethodDescriptor(\n      name: \"DeleteProcess\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/DeleteProcess\",\n      type: GRPCCallType.unary\n    )\n\n    public static let startProcess = GRPCMethodDescriptor(\n      name: \"StartProcess\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/StartProcess\",\n      type: GRPCCallType.unary\n    )\n\n    public static let killProcess = GRPCMethodDescriptor(\n      name: \"KillProcess\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/KillProcess\",\n      type: GRPCCallType.unary\n    )\n\n    public static let waitProcess = GRPCMethodDescriptor(\n      name: \"WaitProcess\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/WaitProcess\",\n      type: GRPCCallType.unary\n    )\n\n    public static let resizeProcess = GRPCMethodDescriptor(\n      name: \"ResizeProcess\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/ResizeProcess\",\n      type: GRPCCallType.unary\n    )\n\n    public static let closeProcessStdin = GRPCMethodDescriptor(\n      name: \"CloseProcessStdin\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/CloseProcessStdin\",\n      type: GRPCCallType.unary\n    )\n\n    public static let containerStatistics = GRPCMethodDescriptor(\n      name: \"ContainerStatistics\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/ContainerStatistics\",\n      type: GRPCCallType.unary\n    )\n\n    public static let proxyVsock = GRPCMethodDescriptor(\n      name: \"ProxyVsock\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/ProxyVsock\",\n      type: GRPCCallType.unary\n    )\n\n    public static let stopVsockProxy = GRPCMethodDescriptor(\n      name: \"StopVsockProxy\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/StopVsockProxy\",\n      type: GRPCCallType.unary\n    )\n\n    public static let ipLinkSet = GRPCMethodDescriptor(\n      name: \"IpLinkSet\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/IpLinkSet\",\n      type: GRPCCallType.unary\n    )\n\n    public static let ipAddrAdd = GRPCMethodDescriptor(\n      name: \"IpAddrAdd\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/IpAddrAdd\",\n      type: GRPCCallType.unary\n    )\n\n    public static let ipRouteAddLink = GRPCMethodDescriptor(\n      name: \"IpRouteAddLink\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/IpRouteAddLink\",\n      type: GRPCCallType.unary\n    )\n\n    public static let ipRouteAddDefault = GRPCMethodDescriptor(\n      name: \"IpRouteAddDefault\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/IpRouteAddDefault\",\n      type: GRPCCallType.unary\n    )\n\n    public static let configureDns = GRPCMethodDescriptor(\n      name: \"ConfigureDns\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/ConfigureDns\",\n      type: GRPCCallType.unary\n    )\n\n    public static let configureHosts = GRPCMethodDescriptor(\n      name: \"ConfigureHosts\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/ConfigureHosts\",\n      type: GRPCCallType.unary\n    )\n\n    public static let sync = GRPCMethodDescriptor(\n      name: \"Sync\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Sync\",\n      type: GRPCCallType.unary\n    )\n\n    public static let kill = GRPCMethodDescriptor(\n      name: \"Kill\",\n      path: \"/com.apple.containerization.sandbox.v3.SandboxContext/Kill\",\n      type: GRPCCallType.unary\n    )\n  }\n}\n"
  },
  {
    "path": "Sources/Containerization/SandboxContext/SandboxContext.pb.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n// DO NOT EDIT.\n// swift-format-ignore-file\n// swiftlint:disable all\n//\n// Generated by the Swift generator plugin for the protocol buffer compiler.\n// Source: SandboxContext.proto\n//\n// For information on using the generated types, please see the documentation:\n//   https://github.com/apple/swift-protobuf/\n\nimport Foundation\nimport SwiftProtobuf\n\n// If the compiler emits an error on this type, it is because this file\n// was generated by a version of the `protoc` Swift plug-in that is\n// incompatible with the version of SwiftProtobuf to which you are linking.\n// Please ensure that you are building against the same version of the API\n// that was used to generate this file.\nfileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {\n  struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}\n  typealias Version = _2\n}\n\n/// Categories of statistics that can be requested.\npublic enum Com_Apple_Containerization_Sandbox_V3_StatCategory: SwiftProtobuf.Enum, Swift.CaseIterable {\n  public typealias RawValue = Int\n  case unspecified // = 0\n  case process // = 1\n  case memory // = 2\n  case cpu // = 3\n  case blockIo // = 4\n  case network // = 5\n  case memoryEvents // = 6\n  case UNRECOGNIZED(Int)\n\n  public init() {\n    self = .unspecified\n  }\n\n  public init?(rawValue: Int) {\n    switch rawValue {\n    case 0: self = .unspecified\n    case 1: self = .process\n    case 2: self = .memory\n    case 3: self = .cpu\n    case 4: self = .blockIo\n    case 5: self = .network\n    case 6: self = .memoryEvents\n    default: self = .UNRECOGNIZED(rawValue)\n    }\n  }\n\n  public var rawValue: Int {\n    switch self {\n    case .unspecified: return 0\n    case .process: return 1\n    case .memory: return 2\n    case .cpu: return 3\n    case .blockIo: return 4\n    case .network: return 5\n    case .memoryEvents: return 6\n    case .UNRECOGNIZED(let i): return i\n    }\n  }\n\n  // The compiler won't synthesize support with the UNRECOGNIZED case.\n  public static let allCases: [Com_Apple_Containerization_Sandbox_V3_StatCategory] = [\n    .unspecified,\n    .process,\n    .memory,\n    .cpu,\n    .blockIo,\n    .network,\n    .memoryEvents,\n  ]\n\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_Stdio: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var stdinPort: Int32 {\n    get {return _stdinPort ?? 0}\n    set {_stdinPort = newValue}\n  }\n  /// Returns true if `stdinPort` has been explicitly set.\n  public var hasStdinPort: Bool {return self._stdinPort != nil}\n  /// Clears the value of `stdinPort`. Subsequent reads from it will return its default value.\n  public mutating func clearStdinPort() {self._stdinPort = nil}\n\n  public var stdoutPort: Int32 {\n    get {return _stdoutPort ?? 0}\n    set {_stdoutPort = newValue}\n  }\n  /// Returns true if `stdoutPort` has been explicitly set.\n  public var hasStdoutPort: Bool {return self._stdoutPort != nil}\n  /// Clears the value of `stdoutPort`. Subsequent reads from it will return its default value.\n  public mutating func clearStdoutPort() {self._stdoutPort = nil}\n\n  public var stderrPort: Int32 {\n    get {return _stderrPort ?? 0}\n    set {_stderrPort = newValue}\n  }\n  /// Returns true if `stderrPort` has been explicitly set.\n  public var hasStderrPort: Bool {return self._stderrPort != nil}\n  /// Clears the value of `stderrPort`. Subsequent reads from it will return its default value.\n  public mutating func clearStderrPort() {self._stderrPort = nil}\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n\n  fileprivate var _stdinPort: Int32? = nil\n  fileprivate var _stdoutPort: Int32? = nil\n  fileprivate var _stderrPort: Int32? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var binaryPath: String = String()\n\n  public var name: String = String()\n\n  public var type: String = String()\n\n  public var offset: String = String()\n\n  public var magic: String = String()\n\n  public var mask: String = String()\n\n  public var flags: String = String()\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_SetTimeRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var sec: Int64 = 0\n\n  public var usec: Int32 = 0\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_SetTimeResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_SysctlRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var settings: Dictionary<String,String> = [:]\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_SysctlResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var id: String = String()\n\n  public var vsockPort: UInt32 = 0\n\n  public var guestPath: String = String()\n\n  public var guestSocketPermissions: UInt32 {\n    get {return _guestSocketPermissions ?? 0}\n    set {_guestSocketPermissions = newValue}\n  }\n  /// Returns true if `guestSocketPermissions` has been explicitly set.\n  public var hasGuestSocketPermissions: Bool {return self._guestSocketPermissions != nil}\n  /// Clears the value of `guestSocketPermissions`. Subsequent reads from it will return its default value.\n  public mutating func clearGuestSocketPermissions() {self._guestSocketPermissions = nil}\n\n  public var action: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest.Action = .into\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public enum Action: SwiftProtobuf.Enum, Swift.CaseIterable {\n    public typealias RawValue = Int\n    case into // = 0\n    case outOf // = 1\n    case UNRECOGNIZED(Int)\n\n    public init() {\n      self = .into\n    }\n\n    public init?(rawValue: Int) {\n      switch rawValue {\n      case 0: self = .into\n      case 1: self = .outOf\n      default: self = .UNRECOGNIZED(rawValue)\n      }\n    }\n\n    public var rawValue: Int {\n      switch self {\n      case .into: return 0\n      case .outOf: return 1\n      case .UNRECOGNIZED(let i): return i\n      }\n    }\n\n    // The compiler won't synthesize support with the UNRECOGNIZED case.\n    public static let allCases: [Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest.Action] = [\n      .into,\n      .outOf,\n    ]\n\n  }\n\n  public init() {}\n\n  fileprivate var _guestSocketPermissions: UInt32? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var id: String = String()\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_MountRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var type: String = String()\n\n  public var source: String = String()\n\n  public var destination: String = String()\n\n  public var options: [String] = []\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_MountResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_UmountRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var path: String = String()\n\n  public var flags: Int32 = 0\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_UmountResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_SetenvRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var key: String = String()\n\n  public var value: String {\n    get {return _value ?? String()}\n    set {_value = newValue}\n  }\n  /// Returns true if `value` has been explicitly set.\n  public var hasValue: Bool {return self._value != nil}\n  /// Clears the value of `value`. Subsequent reads from it will return its default value.\n  public mutating func clearValue() {self._value = nil}\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n\n  fileprivate var _value: String? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_SetenvResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_GetenvRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var key: String = String()\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_GetenvResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var value: String {\n    get {return _value ?? String()}\n    set {_value = newValue}\n  }\n  /// Returns true if `value` has been explicitly set.\n  public var hasValue: Bool {return self._value != nil}\n  /// Clears the value of `value`. Subsequent reads from it will return its default value.\n  public mutating func clearValue() {self._value = nil}\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n\n  fileprivate var _value: String? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest: @unchecked Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var id: String = String()\n\n  public var containerID: String {\n    get {return _containerID ?? String()}\n    set {_containerID = newValue}\n  }\n  /// Returns true if `containerID` has been explicitly set.\n  public var hasContainerID: Bool {return self._containerID != nil}\n  /// Clears the value of `containerID`. Subsequent reads from it will return its default value.\n  public mutating func clearContainerID() {self._containerID = nil}\n\n  public var stdin: UInt32 {\n    get {return _stdin ?? 0}\n    set {_stdin = newValue}\n  }\n  /// Returns true if `stdin` has been explicitly set.\n  public var hasStdin: Bool {return self._stdin != nil}\n  /// Clears the value of `stdin`. Subsequent reads from it will return its default value.\n  public mutating func clearStdin() {self._stdin = nil}\n\n  public var stdout: UInt32 {\n    get {return _stdout ?? 0}\n    set {_stdout = newValue}\n  }\n  /// Returns true if `stdout` has been explicitly set.\n  public var hasStdout: Bool {return self._stdout != nil}\n  /// Clears the value of `stdout`. Subsequent reads from it will return its default value.\n  public mutating func clearStdout() {self._stdout = nil}\n\n  public var stderr: UInt32 {\n    get {return _stderr ?? 0}\n    set {_stderr = newValue}\n  }\n  /// Returns true if `stderr` has been explicitly set.\n  public var hasStderr: Bool {return self._stderr != nil}\n  /// Clears the value of `stderr`. Subsequent reads from it will return its default value.\n  public mutating func clearStderr() {self._stderr = nil}\n\n  public var ociRuntimePath: String {\n    get {return _ociRuntimePath ?? String()}\n    set {_ociRuntimePath = newValue}\n  }\n  /// Returns true if `ociRuntimePath` has been explicitly set.\n  public var hasOciRuntimePath: Bool {return self._ociRuntimePath != nil}\n  /// Clears the value of `ociRuntimePath`. Subsequent reads from it will return its default value.\n  public mutating func clearOciRuntimePath() {self._ociRuntimePath = nil}\n\n  public var configuration: Data = Data()\n\n  public var options: Data {\n    get {return _options ?? Data()}\n    set {_options = newValue}\n  }\n  /// Returns true if `options` has been explicitly set.\n  public var hasOptions: Bool {return self._options != nil}\n  /// Clears the value of `options`. Subsequent reads from it will return its default value.\n  public mutating func clearOptions() {self._options = nil}\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n\n  fileprivate var _containerID: String? = nil\n  fileprivate var _stdin: UInt32? = nil\n  fileprivate var _stdout: UInt32? = nil\n  fileprivate var _stderr: UInt32? = nil\n  fileprivate var _ociRuntimePath: String? = nil\n  fileprivate var _options: Data? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var id: String = String()\n\n  public var containerID: String {\n    get {return _containerID ?? String()}\n    set {_containerID = newValue}\n  }\n  /// Returns true if `containerID` has been explicitly set.\n  public var hasContainerID: Bool {return self._containerID != nil}\n  /// Clears the value of `containerID`. Subsequent reads from it will return its default value.\n  public mutating func clearContainerID() {self._containerID = nil}\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n\n  fileprivate var _containerID: String? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var exitCode: Int32 = 0\n\n  public var exitedAt: SwiftProtobuf.Google_Protobuf_Timestamp {\n    get {return _exitedAt ?? SwiftProtobuf.Google_Protobuf_Timestamp()}\n    set {_exitedAt = newValue}\n  }\n  /// Returns true if `exitedAt` has been explicitly set.\n  public var hasExitedAt: Bool {return self._exitedAt != nil}\n  /// Clears the value of `exitedAt`. Subsequent reads from it will return its default value.\n  public mutating func clearExitedAt() {self._exitedAt = nil}\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n\n  fileprivate var _exitedAt: SwiftProtobuf.Google_Protobuf_Timestamp? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var id: String = String()\n\n  public var containerID: String {\n    get {return _containerID ?? String()}\n    set {_containerID = newValue}\n  }\n  /// Returns true if `containerID` has been explicitly set.\n  public var hasContainerID: Bool {return self._containerID != nil}\n  /// Clears the value of `containerID`. Subsequent reads from it will return its default value.\n  public mutating func clearContainerID() {self._containerID = nil}\n\n  public var rows: UInt32 = 0\n\n  public var columns: UInt32 = 0\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n\n  fileprivate var _containerID: String? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var id: String = String()\n\n  public var containerID: String {\n    get {return _containerID ?? String()}\n    set {_containerID = newValue}\n  }\n  /// Returns true if `containerID` has been explicitly set.\n  public var hasContainerID: Bool {return self._containerID != nil}\n  /// Clears the value of `containerID`. Subsequent reads from it will return its default value.\n  public mutating func clearContainerID() {self._containerID = nil}\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n\n  fileprivate var _containerID: String? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_StartProcessRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var id: String = String()\n\n  public var containerID: String {\n    get {return _containerID ?? String()}\n    set {_containerID = newValue}\n  }\n  /// Returns true if `containerID` has been explicitly set.\n  public var hasContainerID: Bool {return self._containerID != nil}\n  /// Clears the value of `containerID`. Subsequent reads from it will return its default value.\n  public mutating func clearContainerID() {self._containerID = nil}\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n\n  fileprivate var _containerID: String? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_StartProcessResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var pid: Int32 = 0\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_KillProcessRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var id: String = String()\n\n  public var containerID: String {\n    get {return _containerID ?? String()}\n    set {_containerID = newValue}\n  }\n  /// Returns true if `containerID` has been explicitly set.\n  public var hasContainerID: Bool {return self._containerID != nil}\n  /// Clears the value of `containerID`. Subsequent reads from it will return its default value.\n  public mutating func clearContainerID() {self._containerID = nil}\n\n  public var signal: Int32 = 0\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n\n  fileprivate var _containerID: String? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_KillProcessResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var result: Int32 = 0\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var id: String = String()\n\n  public var containerID: String {\n    get {return _containerID ?? String()}\n    set {_containerID = newValue}\n  }\n  /// Returns true if `containerID` has been explicitly set.\n  public var hasContainerID: Bool {return self._containerID != nil}\n  /// Clears the value of `containerID`. Subsequent reads from it will return its default value.\n  public mutating func clearContainerID() {self._containerID = nil}\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n\n  fileprivate var _containerID: String? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_MkdirRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var path: String = String()\n\n  public var all: Bool = false\n\n  public var perms: UInt32 = 0\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_MkdirResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_WriteFileRequest: @unchecked Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var path: String = String()\n\n  public var data: Data = Data()\n\n  public var mode: UInt32 = 0\n\n  public var flags: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest.WriteFileFlags {\n    get {return _flags ?? Com_Apple_Containerization_Sandbox_V3_WriteFileRequest.WriteFileFlags()}\n    set {_flags = newValue}\n  }\n  /// Returns true if `flags` has been explicitly set.\n  public var hasFlags: Bool {return self._flags != nil}\n  /// Clears the value of `flags`. Subsequent reads from it will return its default value.\n  public mutating func clearFlags() {self._flags = nil}\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public struct WriteFileFlags: Sendable {\n    // SwiftProtobuf.Message conformance is added in an extension below. See the\n    // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n    // methods supported on all messages.\n\n    public var createParentDirs: Bool = false\n\n    public var append: Bool = false\n\n    public var createIfMissing: Bool = false\n\n    public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n    public init() {}\n  }\n\n  public init() {}\n\n  fileprivate var _flags: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest.WriteFileFlags? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_WriteFileResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_CopyRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  /// Direction of the copy operation.\n  public var direction: Com_Apple_Containerization_Sandbox_V3_CopyRequest.Direction = .copyIn\n\n  /// Path in the guest (destination for COPY_IN, source for COPY_OUT).\n  public var path: String = String()\n\n  /// File mode for single-file COPY_IN (defaults to 0644 if not set).\n  public var mode: UInt32 = 0\n\n  /// Create parent directories if they don't exist.\n  public var createParents: Bool = false\n\n  /// Vsock port the host is listening on for data transfer.\n  public var vsockPort: UInt32 = 0\n\n  /// For COPY_IN: indicates the data arriving on vsock is a tar+gzip archive.\n  public var isArchive: Bool = false\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public enum Direction: SwiftProtobuf.Enum, Swift.CaseIterable {\n    public typealias RawValue = Int\n\n    /// Copy from host into guest.\n    case copyIn // = 0\n\n    /// Copy from guest to host.\n    case copyOut // = 1\n    case UNRECOGNIZED(Int)\n\n    public init() {\n      self = .copyIn\n    }\n\n    public init?(rawValue: Int) {\n      switch rawValue {\n      case 0: self = .copyIn\n      case 1: self = .copyOut\n      default: self = .UNRECOGNIZED(rawValue)\n      }\n    }\n\n    public var rawValue: Int {\n      switch self {\n      case .copyIn: return 0\n      case .copyOut: return 1\n      case .UNRECOGNIZED(let i): return i\n      }\n    }\n\n    // The compiler won't synthesize support with the UNRECOGNIZED case.\n    public static let allCases: [Com_Apple_Containerization_Sandbox_V3_CopyRequest.Direction] = [\n      .copyIn,\n      .copyOut,\n    ]\n\n  }\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_CopyResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  /// What this response represents.\n  public var status: Com_Apple_Containerization_Sandbox_V3_CopyResponse.Status = .metadata\n\n  /// For COPY_OUT METADATA: indicates the data on vsock will be a tar+gzip archive.\n  public var isArchive: Bool = false\n\n  /// For COPY_OUT METADATA: total size in bytes (0 if unknown, e.g. for archives).\n  public var totalSize: UInt64 = 0\n\n  /// Non-empty if an error occurred.\n  public var error: String = String()\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public enum Status: SwiftProtobuf.Enum, Swift.CaseIterable {\n    public typealias RawValue = Int\n\n    /// Transfer metadata (first message for COPY_OUT: is_archive, total_size).\n    case metadata // = 0\n\n    /// Data transfer completed successfully.\n    case complete // = 1\n    case UNRECOGNIZED(Int)\n\n    public init() {\n      self = .metadata\n    }\n\n    public init?(rawValue: Int) {\n      switch rawValue {\n      case 0: self = .metadata\n      case 1: self = .complete\n      default: self = .UNRECOGNIZED(rawValue)\n      }\n    }\n\n    public var rawValue: Int {\n      switch self {\n      case .metadata: return 0\n      case .complete: return 1\n      case .UNRECOGNIZED(let i): return i\n      }\n    }\n\n    // The compiler won't synthesize support with the UNRECOGNIZED case.\n    public static let allCases: [Com_Apple_Containerization_Sandbox_V3_CopyResponse.Status] = [\n      .metadata,\n      .complete,\n    ]\n\n  }\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var interface: String = String()\n\n  public var up: Bool = false\n\n  public var mtu: UInt32 {\n    get {return _mtu ?? 0}\n    set {_mtu = newValue}\n  }\n  /// Returns true if `mtu` has been explicitly set.\n  public var hasMtu: Bool {return self._mtu != nil}\n  /// Clears the value of `mtu`. Subsequent reads from it will return its default value.\n  public mutating func clearMtu() {self._mtu = nil}\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n\n  fileprivate var _mtu: UInt32? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var interface: String = String()\n\n  public var ipv4Address: String = String()\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var interface: String = String()\n\n  public var dstIpv4Addr: String = String()\n\n  public var srcIpv4Addr: String = String()\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var interface: String = String()\n\n  public var ipv4Gateway: String = String()\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var location: String = String()\n\n  public var nameservers: [String] = []\n\n  public var domain: String {\n    get {return _domain ?? String()}\n    set {_domain = newValue}\n  }\n  /// Returns true if `domain` has been explicitly set.\n  public var hasDomain: Bool {return self._domain != nil}\n  /// Clears the value of `domain`. Subsequent reads from it will return its default value.\n  public mutating func clearDomain() {self._domain = nil}\n\n  public var searchDomains: [String] = []\n\n  public var options: [String] = []\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n\n  fileprivate var _domain: String? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var location: String = String()\n\n  public var entries: [Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest.HostsEntry] = []\n\n  public var comment: String {\n    get {return _comment ?? String()}\n    set {_comment = newValue}\n  }\n  /// Returns true if `comment` has been explicitly set.\n  public var hasComment: Bool {return self._comment != nil}\n  /// Clears the value of `comment`. Subsequent reads from it will return its default value.\n  public mutating func clearComment() {self._comment = nil}\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public struct HostsEntry: Sendable {\n    // SwiftProtobuf.Message conformance is added in an extension below. See the\n    // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n    // methods supported on all messages.\n\n    public var ipAddress: String = String()\n\n    public var hostnames: [String] = []\n\n    public var comment: String {\n      get {return _comment ?? String()}\n      set {_comment = newValue}\n    }\n    /// Returns true if `comment` has been explicitly set.\n    public var hasComment: Bool {return self._comment != nil}\n    /// Clears the value of `comment`. Subsequent reads from it will return its default value.\n    public mutating func clearComment() {self._comment = nil}\n\n    public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n    public init() {}\n\n    fileprivate var _comment: String? = nil\n  }\n\n  public init() {}\n\n  fileprivate var _comment: String? = nil\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_SyncRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_SyncResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_KillRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var pid: Int32 = 0\n\n  public var signal: Int32 = 0\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_KillResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var result: Int32 = 0\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  /// Empty = all containers\n  public var containerIds: [String] = []\n\n  /// Empty = all categories\n  public var categories: [Com_Apple_Containerization_Sandbox_V3_StatCategory] = []\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var containers: [Com_Apple_Containerization_Sandbox_V3_ContainerStats] = []\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_ContainerStats: @unchecked Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var containerID: String {\n    get {return _storage._containerID}\n    set {_uniqueStorage()._containerID = newValue}\n  }\n\n  public var process: Com_Apple_Containerization_Sandbox_V3_ProcessStats {\n    get {return _storage._process ?? Com_Apple_Containerization_Sandbox_V3_ProcessStats()}\n    set {_uniqueStorage()._process = newValue}\n  }\n  /// Returns true if `process` has been explicitly set.\n  public var hasProcess: Bool {return _storage._process != nil}\n  /// Clears the value of `process`. Subsequent reads from it will return its default value.\n  public mutating func clearProcess() {_uniqueStorage()._process = nil}\n\n  public var memory: Com_Apple_Containerization_Sandbox_V3_MemoryStats {\n    get {return _storage._memory ?? Com_Apple_Containerization_Sandbox_V3_MemoryStats()}\n    set {_uniqueStorage()._memory = newValue}\n  }\n  /// Returns true if `memory` has been explicitly set.\n  public var hasMemory: Bool {return _storage._memory != nil}\n  /// Clears the value of `memory`. Subsequent reads from it will return its default value.\n  public mutating func clearMemory() {_uniqueStorage()._memory = nil}\n\n  public var cpu: Com_Apple_Containerization_Sandbox_V3_CPUStats {\n    get {return _storage._cpu ?? Com_Apple_Containerization_Sandbox_V3_CPUStats()}\n    set {_uniqueStorage()._cpu = newValue}\n  }\n  /// Returns true if `cpu` has been explicitly set.\n  public var hasCpu: Bool {return _storage._cpu != nil}\n  /// Clears the value of `cpu`. Subsequent reads from it will return its default value.\n  public mutating func clearCpu() {_uniqueStorage()._cpu = nil}\n\n  public var blockIo: Com_Apple_Containerization_Sandbox_V3_BlockIOStats {\n    get {return _storage._blockIo ?? Com_Apple_Containerization_Sandbox_V3_BlockIOStats()}\n    set {_uniqueStorage()._blockIo = newValue}\n  }\n  /// Returns true if `blockIo` has been explicitly set.\n  public var hasBlockIo: Bool {return _storage._blockIo != nil}\n  /// Clears the value of `blockIo`. Subsequent reads from it will return its default value.\n  public mutating func clearBlockIo() {_uniqueStorage()._blockIo = nil}\n\n  public var networks: [Com_Apple_Containerization_Sandbox_V3_NetworkStats] {\n    get {return _storage._networks}\n    set {_uniqueStorage()._networks = newValue}\n  }\n\n  public var memoryEvents: Com_Apple_Containerization_Sandbox_V3_MemoryEventStats {\n    get {return _storage._memoryEvents ?? Com_Apple_Containerization_Sandbox_V3_MemoryEventStats()}\n    set {_uniqueStorage()._memoryEvents = newValue}\n  }\n  /// Returns true if `memoryEvents` has been explicitly set.\n  public var hasMemoryEvents: Bool {return _storage._memoryEvents != nil}\n  /// Clears the value of `memoryEvents`. Subsequent reads from it will return its default value.\n  public mutating func clearMemoryEvents() {_uniqueStorage()._memoryEvents = nil}\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n\n  fileprivate var _storage = _StorageClass.defaultInstance\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_ProcessStats: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var current: UInt64 = 0\n\n  /// 0 or max value = unlimited\n  public var limit: UInt64 = 0\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_MemoryStats: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var usageBytes: UInt64 = 0\n\n  public var limitBytes: UInt64 = 0\n\n  public var swapUsageBytes: UInt64 = 0\n\n  public var swapLimitBytes: UInt64 = 0\n\n  public var cacheBytes: UInt64 = 0\n\n  public var kernelStackBytes: UInt64 = 0\n\n  public var slabBytes: UInt64 = 0\n\n  public var pageFaults: UInt64 = 0\n\n  public var majorPageFaults: UInt64 = 0\n\n  public var inactiveFile: UInt64 = 0\n\n  public var anon: UInt64 = 0\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_CPUStats: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var usageUsec: UInt64 = 0\n\n  public var userUsec: UInt64 = 0\n\n  public var systemUsec: UInt64 = 0\n\n  public var throttlingPeriods: UInt64 = 0\n\n  public var throttledPeriods: UInt64 = 0\n\n  public var throttledTimeUsec: UInt64 = 0\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_BlockIOStats: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var devices: [Com_Apple_Containerization_Sandbox_V3_BlockIOEntry] = []\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_BlockIOEntry: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var major: UInt64 = 0\n\n  public var minor: UInt64 = 0\n\n  public var readBytes: UInt64 = 0\n\n  public var writeBytes: UInt64 = 0\n\n  public var readOperations: UInt64 = 0\n\n  public var writeOperations: UInt64 = 0\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\npublic struct Com_Apple_Containerization_Sandbox_V3_NetworkStats: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  public var interface: String = String()\n\n  public var receivedPackets: UInt64 = 0\n\n  public var transmittedPackets: UInt64 = 0\n\n  public var receivedBytes: UInt64 = 0\n\n  public var transmittedBytes: UInt64 = 0\n\n  public var receivedErrors: UInt64 = 0\n\n  public var transmittedErrors: UInt64 = 0\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\n/// Memory event counters from cgroup2's memory.events file.\npublic struct Com_Apple_Containerization_Sandbox_V3_MemoryEventStats: Sendable {\n  // SwiftProtobuf.Message conformance is added in an extension below. See the\n  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for\n  // methods supported on all messages.\n\n  /// Number of times the cgroup was reclaimed due to low memory.\n  public var low: UInt64 = 0\n\n  /// Number of times the cgroup exceeded its high memory limit.\n  public var high: UInt64 = 0\n\n  /// Number of times the cgroup hit its max memory limit.\n  public var max: UInt64 = 0\n\n  /// Number of times the cgroup triggered OOM.\n  public var oom: UInt64 = 0\n\n  /// Number of processes killed by OOM killer.\n  public var oomKill: UInt64 = 0\n\n  /// Number of times charge for memory failed because of limit.\n  public var oomGroupKill: UInt64 = 0\n\n  public var unknownFields = SwiftProtobuf.UnknownStorage()\n\n  public init() {}\n}\n\n// MARK: - Code below here is support for the SwiftProtobuf runtime.\n\nfileprivate let _protobuf_package = \"com.apple.containerization.sandbox.v3\"\n\nextension Com_Apple_Containerization_Sandbox_V3_StatCategory: SwiftProtobuf._ProtoNameProviding {\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    0: .same(proto: \"STAT_CATEGORY_UNSPECIFIED\"),\n    1: .same(proto: \"STAT_CATEGORY_PROCESS\"),\n    2: .same(proto: \"STAT_CATEGORY_MEMORY\"),\n    3: .same(proto: \"STAT_CATEGORY_CPU\"),\n    4: .same(proto: \"STAT_CATEGORY_BLOCK_IO\"),\n    5: .same(proto: \"STAT_CATEGORY_NETWORK\"),\n    6: .same(proto: \"STAT_CATEGORY_MEMORY_EVENTS\"),\n  ]\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_Stdio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".Stdio\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"stdinPort\"),\n    2: .same(proto: \"stdoutPort\"),\n    3: .same(proto: \"stderrPort\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularInt32Field(value: &self._stdinPort) }()\n      case 2: try { try decoder.decodeSingularInt32Field(value: &self._stdoutPort) }()\n      case 3: try { try decoder.decodeSingularInt32Field(value: &self._stderrPort) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    try { if let v = self._stdinPort {\n      try visitor.visitSingularInt32Field(value: v, fieldNumber: 1)\n    } }()\n    try { if let v = self._stdoutPort {\n      try visitor.visitSingularInt32Field(value: v, fieldNumber: 2)\n    } }()\n    try { if let v = self._stderrPort {\n      try visitor.visitSingularInt32Field(value: v, fieldNumber: 3)\n    } }()\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_Stdio, rhs: Com_Apple_Containerization_Sandbox_V3_Stdio) -> Bool {\n    if lhs._stdinPort != rhs._stdinPort {return false}\n    if lhs._stdoutPort != rhs._stdoutPort {return false}\n    if lhs._stderrPort != rhs._stderrPort {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".SetupEmulatorRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .standard(proto: \"binary_path\"),\n    2: .same(proto: \"name\"),\n    3: .same(proto: \"type\"),\n    4: .same(proto: \"offset\"),\n    5: .same(proto: \"magic\"),\n    6: .same(proto: \"mask\"),\n    7: .same(proto: \"flags\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.binaryPath) }()\n      case 2: try { try decoder.decodeSingularStringField(value: &self.name) }()\n      case 3: try { try decoder.decodeSingularStringField(value: &self.type) }()\n      case 4: try { try decoder.decodeSingularStringField(value: &self.offset) }()\n      case 5: try { try decoder.decodeSingularStringField(value: &self.magic) }()\n      case 6: try { try decoder.decodeSingularStringField(value: &self.mask) }()\n      case 7: try { try decoder.decodeSingularStringField(value: &self.flags) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if !self.binaryPath.isEmpty {\n      try visitor.visitSingularStringField(value: self.binaryPath, fieldNumber: 1)\n    }\n    if !self.name.isEmpty {\n      try visitor.visitSingularStringField(value: self.name, fieldNumber: 2)\n    }\n    if !self.type.isEmpty {\n      try visitor.visitSingularStringField(value: self.type, fieldNumber: 3)\n    }\n    if !self.offset.isEmpty {\n      try visitor.visitSingularStringField(value: self.offset, fieldNumber: 4)\n    }\n    if !self.magic.isEmpty {\n      try visitor.visitSingularStringField(value: self.magic, fieldNumber: 5)\n    }\n    if !self.mask.isEmpty {\n      try visitor.visitSingularStringField(value: self.mask, fieldNumber: 6)\n    }\n    if !self.flags.isEmpty {\n      try visitor.visitSingularStringField(value: self.flags, fieldNumber: 7)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest, rhs: Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest) -> Bool {\n    if lhs.binaryPath != rhs.binaryPath {return false}\n    if lhs.name != rhs.name {return false}\n    if lhs.type != rhs.type {return false}\n    if lhs.offset != rhs.offset {return false}\n    if lhs.magic != rhs.magic {return false}\n    if lhs.mask != rhs.mask {return false}\n    if lhs.flags != rhs.flags {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".SetupEmulatorResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse, rhs: Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_SetTimeRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".SetTimeRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"sec\"),\n    2: .same(proto: \"usec\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularInt64Field(value: &self.sec) }()\n      case 2: try { try decoder.decodeSingularInt32Field(value: &self.usec) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if self.sec != 0 {\n      try visitor.visitSingularInt64Field(value: self.sec, fieldNumber: 1)\n    }\n    if self.usec != 0 {\n      try visitor.visitSingularInt32Field(value: self.usec, fieldNumber: 2)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_SetTimeRequest, rhs: Com_Apple_Containerization_Sandbox_V3_SetTimeRequest) -> Bool {\n    if lhs.sec != rhs.sec {return false}\n    if lhs.usec != rhs.usec {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_SetTimeResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".SetTimeResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_SetTimeResponse, rhs: Com_Apple_Containerization_Sandbox_V3_SetTimeResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_SysctlRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".SysctlRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"settings\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeMapField(fieldType: SwiftProtobuf._ProtobufMap<SwiftProtobuf.ProtobufString,SwiftProtobuf.ProtobufString>.self, value: &self.settings) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if !self.settings.isEmpty {\n      try visitor.visitMapField(fieldType: SwiftProtobuf._ProtobufMap<SwiftProtobuf.ProtobufString,SwiftProtobuf.ProtobufString>.self, value: self.settings, fieldNumber: 1)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_SysctlRequest, rhs: Com_Apple_Containerization_Sandbox_V3_SysctlRequest) -> Bool {\n    if lhs.settings != rhs.settings {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_SysctlResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".SysctlResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_SysctlResponse, rhs: Com_Apple_Containerization_Sandbox_V3_SysctlResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".ProxyVsockRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"id\"),\n    2: .standard(proto: \"vsock_port\"),\n    3: .same(proto: \"guestPath\"),\n    4: .same(proto: \"guestSocketPermissions\"),\n    5: .same(proto: \"action\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.id) }()\n      case 2: try { try decoder.decodeSingularUInt32Field(value: &self.vsockPort) }()\n      case 3: try { try decoder.decodeSingularStringField(value: &self.guestPath) }()\n      case 4: try { try decoder.decodeSingularUInt32Field(value: &self._guestSocketPermissions) }()\n      case 5: try { try decoder.decodeSingularEnumField(value: &self.action) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    if !self.id.isEmpty {\n      try visitor.visitSingularStringField(value: self.id, fieldNumber: 1)\n    }\n    if self.vsockPort != 0 {\n      try visitor.visitSingularUInt32Field(value: self.vsockPort, fieldNumber: 2)\n    }\n    if !self.guestPath.isEmpty {\n      try visitor.visitSingularStringField(value: self.guestPath, fieldNumber: 3)\n    }\n    try { if let v = self._guestSocketPermissions {\n      try visitor.visitSingularUInt32Field(value: v, fieldNumber: 4)\n    } }()\n    if self.action != .into {\n      try visitor.visitSingularEnumField(value: self.action, fieldNumber: 5)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest, rhs: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest) -> Bool {\n    if lhs.id != rhs.id {return false}\n    if lhs.vsockPort != rhs.vsockPort {return false}\n    if lhs.guestPath != rhs.guestPath {return false}\n    if lhs._guestSocketPermissions != rhs._guestSocketPermissions {return false}\n    if lhs.action != rhs.action {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest.Action: SwiftProtobuf._ProtoNameProviding {\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    0: .same(proto: \"INTO\"),\n    1: .same(proto: \"OUT_OF\"),\n  ]\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".ProxyVsockResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse, rhs: Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".StopVsockProxyRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"id\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.id) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if !self.id.isEmpty {\n      try visitor.visitSingularStringField(value: self.id, fieldNumber: 1)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest, rhs: Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest) -> Bool {\n    if lhs.id != rhs.id {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".StopVsockProxyResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse, rhs: Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_MountRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".MountRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"type\"),\n    2: .same(proto: \"source\"),\n    3: .same(proto: \"destination\"),\n    4: .same(proto: \"options\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.type) }()\n      case 2: try { try decoder.decodeSingularStringField(value: &self.source) }()\n      case 3: try { try decoder.decodeSingularStringField(value: &self.destination) }()\n      case 4: try { try decoder.decodeRepeatedStringField(value: &self.options) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if !self.type.isEmpty {\n      try visitor.visitSingularStringField(value: self.type, fieldNumber: 1)\n    }\n    if !self.source.isEmpty {\n      try visitor.visitSingularStringField(value: self.source, fieldNumber: 2)\n    }\n    if !self.destination.isEmpty {\n      try visitor.visitSingularStringField(value: self.destination, fieldNumber: 3)\n    }\n    if !self.options.isEmpty {\n      try visitor.visitRepeatedStringField(value: self.options, fieldNumber: 4)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_MountRequest, rhs: Com_Apple_Containerization_Sandbox_V3_MountRequest) -> Bool {\n    if lhs.type != rhs.type {return false}\n    if lhs.source != rhs.source {return false}\n    if lhs.destination != rhs.destination {return false}\n    if lhs.options != rhs.options {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_MountResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".MountResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_MountResponse, rhs: Com_Apple_Containerization_Sandbox_V3_MountResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_UmountRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".UmountRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"path\"),\n    2: .same(proto: \"flags\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.path) }()\n      case 2: try { try decoder.decodeSingularInt32Field(value: &self.flags) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if !self.path.isEmpty {\n      try visitor.visitSingularStringField(value: self.path, fieldNumber: 1)\n    }\n    if self.flags != 0 {\n      try visitor.visitSingularInt32Field(value: self.flags, fieldNumber: 2)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_UmountRequest, rhs: Com_Apple_Containerization_Sandbox_V3_UmountRequest) -> Bool {\n    if lhs.path != rhs.path {return false}\n    if lhs.flags != rhs.flags {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_UmountResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".UmountResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_UmountResponse, rhs: Com_Apple_Containerization_Sandbox_V3_UmountResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_SetenvRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".SetenvRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"key\"),\n    2: .same(proto: \"value\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.key) }()\n      case 2: try { try decoder.decodeSingularStringField(value: &self._value) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    if !self.key.isEmpty {\n      try visitor.visitSingularStringField(value: self.key, fieldNumber: 1)\n    }\n    try { if let v = self._value {\n      try visitor.visitSingularStringField(value: v, fieldNumber: 2)\n    } }()\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_SetenvRequest, rhs: Com_Apple_Containerization_Sandbox_V3_SetenvRequest) -> Bool {\n    if lhs.key != rhs.key {return false}\n    if lhs._value != rhs._value {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_SetenvResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".SetenvResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_SetenvResponse, rhs: Com_Apple_Containerization_Sandbox_V3_SetenvResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_GetenvRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".GetenvRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"key\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.key) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if !self.key.isEmpty {\n      try visitor.visitSingularStringField(value: self.key, fieldNumber: 1)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_GetenvRequest, rhs: Com_Apple_Containerization_Sandbox_V3_GetenvRequest) -> Bool {\n    if lhs.key != rhs.key {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_GetenvResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".GetenvResponse\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"value\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self._value) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    try { if let v = self._value {\n      try visitor.visitSingularStringField(value: v, fieldNumber: 1)\n    } }()\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_GetenvResponse, rhs: Com_Apple_Containerization_Sandbox_V3_GetenvResponse) -> Bool {\n    if lhs._value != rhs._value {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".CreateProcessRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"id\"),\n    2: .same(proto: \"containerID\"),\n    3: .same(proto: \"stdin\"),\n    4: .same(proto: \"stdout\"),\n    5: .same(proto: \"stderr\"),\n    6: .same(proto: \"ociRuntimePath\"),\n    7: .same(proto: \"configuration\"),\n    8: .same(proto: \"options\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.id) }()\n      case 2: try { try decoder.decodeSingularStringField(value: &self._containerID) }()\n      case 3: try { try decoder.decodeSingularUInt32Field(value: &self._stdin) }()\n      case 4: try { try decoder.decodeSingularUInt32Field(value: &self._stdout) }()\n      case 5: try { try decoder.decodeSingularUInt32Field(value: &self._stderr) }()\n      case 6: try { try decoder.decodeSingularStringField(value: &self._ociRuntimePath) }()\n      case 7: try { try decoder.decodeSingularBytesField(value: &self.configuration) }()\n      case 8: try { try decoder.decodeSingularBytesField(value: &self._options) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    if !self.id.isEmpty {\n      try visitor.visitSingularStringField(value: self.id, fieldNumber: 1)\n    }\n    try { if let v = self._containerID {\n      try visitor.visitSingularStringField(value: v, fieldNumber: 2)\n    } }()\n    try { if let v = self._stdin {\n      try visitor.visitSingularUInt32Field(value: v, fieldNumber: 3)\n    } }()\n    try { if let v = self._stdout {\n      try visitor.visitSingularUInt32Field(value: v, fieldNumber: 4)\n    } }()\n    try { if let v = self._stderr {\n      try visitor.visitSingularUInt32Field(value: v, fieldNumber: 5)\n    } }()\n    try { if let v = self._ociRuntimePath {\n      try visitor.visitSingularStringField(value: v, fieldNumber: 6)\n    } }()\n    if !self.configuration.isEmpty {\n      try visitor.visitSingularBytesField(value: self.configuration, fieldNumber: 7)\n    }\n    try { if let v = self._options {\n      try visitor.visitSingularBytesField(value: v, fieldNumber: 8)\n    } }()\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest, rhs: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest) -> Bool {\n    if lhs.id != rhs.id {return false}\n    if lhs._containerID != rhs._containerID {return false}\n    if lhs._stdin != rhs._stdin {return false}\n    if lhs._stdout != rhs._stdout {return false}\n    if lhs._stderr != rhs._stderr {return false}\n    if lhs._ociRuntimePath != rhs._ociRuntimePath {return false}\n    if lhs.configuration != rhs.configuration {return false}\n    if lhs._options != rhs._options {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".CreateProcessResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse, rhs: Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".WaitProcessRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"id\"),\n    2: .same(proto: \"containerID\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.id) }()\n      case 2: try { try decoder.decodeSingularStringField(value: &self._containerID) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    if !self.id.isEmpty {\n      try visitor.visitSingularStringField(value: self.id, fieldNumber: 1)\n    }\n    try { if let v = self._containerID {\n      try visitor.visitSingularStringField(value: v, fieldNumber: 2)\n    } }()\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest, rhs: Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest) -> Bool {\n    if lhs.id != rhs.id {return false}\n    if lhs._containerID != rhs._containerID {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".WaitProcessResponse\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"exitCode\"),\n    2: .standard(proto: \"exited_at\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularInt32Field(value: &self.exitCode) }()\n      case 2: try { try decoder.decodeSingularMessageField(value: &self._exitedAt) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    if self.exitCode != 0 {\n      try visitor.visitSingularInt32Field(value: self.exitCode, fieldNumber: 1)\n    }\n    try { if let v = self._exitedAt {\n      try visitor.visitSingularMessageField(value: v, fieldNumber: 2)\n    } }()\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse, rhs: Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse) -> Bool {\n    if lhs.exitCode != rhs.exitCode {return false}\n    if lhs._exitedAt != rhs._exitedAt {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".ResizeProcessRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"id\"),\n    2: .same(proto: \"containerID\"),\n    3: .same(proto: \"rows\"),\n    4: .same(proto: \"columns\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.id) }()\n      case 2: try { try decoder.decodeSingularStringField(value: &self._containerID) }()\n      case 3: try { try decoder.decodeSingularUInt32Field(value: &self.rows) }()\n      case 4: try { try decoder.decodeSingularUInt32Field(value: &self.columns) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    if !self.id.isEmpty {\n      try visitor.visitSingularStringField(value: self.id, fieldNumber: 1)\n    }\n    try { if let v = self._containerID {\n      try visitor.visitSingularStringField(value: v, fieldNumber: 2)\n    } }()\n    if self.rows != 0 {\n      try visitor.visitSingularUInt32Field(value: self.rows, fieldNumber: 3)\n    }\n    if self.columns != 0 {\n      try visitor.visitSingularUInt32Field(value: self.columns, fieldNumber: 4)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest, rhs: Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest) -> Bool {\n    if lhs.id != rhs.id {return false}\n    if lhs._containerID != rhs._containerID {return false}\n    if lhs.rows != rhs.rows {return false}\n    if lhs.columns != rhs.columns {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".ResizeProcessResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse, rhs: Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".DeleteProcessRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"id\"),\n    2: .same(proto: \"containerID\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.id) }()\n      case 2: try { try decoder.decodeSingularStringField(value: &self._containerID) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    if !self.id.isEmpty {\n      try visitor.visitSingularStringField(value: self.id, fieldNumber: 1)\n    }\n    try { if let v = self._containerID {\n      try visitor.visitSingularStringField(value: v, fieldNumber: 2)\n    } }()\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest, rhs: Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest) -> Bool {\n    if lhs.id != rhs.id {return false}\n    if lhs._containerID != rhs._containerID {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".DeleteProcessResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse, rhs: Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_StartProcessRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".StartProcessRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"id\"),\n    2: .same(proto: \"containerID\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.id) }()\n      case 2: try { try decoder.decodeSingularStringField(value: &self._containerID) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    if !self.id.isEmpty {\n      try visitor.visitSingularStringField(value: self.id, fieldNumber: 1)\n    }\n    try { if let v = self._containerID {\n      try visitor.visitSingularStringField(value: v, fieldNumber: 2)\n    } }()\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_StartProcessRequest, rhs: Com_Apple_Containerization_Sandbox_V3_StartProcessRequest) -> Bool {\n    if lhs.id != rhs.id {return false}\n    if lhs._containerID != rhs._containerID {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_StartProcessResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".StartProcessResponse\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"pid\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularInt32Field(value: &self.pid) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if self.pid != 0 {\n      try visitor.visitSingularInt32Field(value: self.pid, fieldNumber: 1)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_StartProcessResponse, rhs: Com_Apple_Containerization_Sandbox_V3_StartProcessResponse) -> Bool {\n    if lhs.pid != rhs.pid {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_KillProcessRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".KillProcessRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"id\"),\n    2: .same(proto: \"containerID\"),\n    3: .same(proto: \"signal\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.id) }()\n      case 2: try { try decoder.decodeSingularStringField(value: &self._containerID) }()\n      case 3: try { try decoder.decodeSingularInt32Field(value: &self.signal) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    if !self.id.isEmpty {\n      try visitor.visitSingularStringField(value: self.id, fieldNumber: 1)\n    }\n    try { if let v = self._containerID {\n      try visitor.visitSingularStringField(value: v, fieldNumber: 2)\n    } }()\n    if self.signal != 0 {\n      try visitor.visitSingularInt32Field(value: self.signal, fieldNumber: 3)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_KillProcessRequest, rhs: Com_Apple_Containerization_Sandbox_V3_KillProcessRequest) -> Bool {\n    if lhs.id != rhs.id {return false}\n    if lhs._containerID != rhs._containerID {return false}\n    if lhs.signal != rhs.signal {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_KillProcessResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".KillProcessResponse\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"result\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularInt32Field(value: &self.result) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if self.result != 0 {\n      try visitor.visitSingularInt32Field(value: self.result, fieldNumber: 1)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_KillProcessResponse, rhs: Com_Apple_Containerization_Sandbox_V3_KillProcessResponse) -> Bool {\n    if lhs.result != rhs.result {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".CloseProcessStdinRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"id\"),\n    2: .same(proto: \"containerID\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.id) }()\n      case 2: try { try decoder.decodeSingularStringField(value: &self._containerID) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    if !self.id.isEmpty {\n      try visitor.visitSingularStringField(value: self.id, fieldNumber: 1)\n    }\n    try { if let v = self._containerID {\n      try visitor.visitSingularStringField(value: v, fieldNumber: 2)\n    } }()\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest, rhs: Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest) -> Bool {\n    if lhs.id != rhs.id {return false}\n    if lhs._containerID != rhs._containerID {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".CloseProcessStdinResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse, rhs: Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_MkdirRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".MkdirRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"path\"),\n    2: .same(proto: \"all\"),\n    3: .same(proto: \"perms\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.path) }()\n      case 2: try { try decoder.decodeSingularBoolField(value: &self.all) }()\n      case 3: try { try decoder.decodeSingularUInt32Field(value: &self.perms) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if !self.path.isEmpty {\n      try visitor.visitSingularStringField(value: self.path, fieldNumber: 1)\n    }\n    if self.all != false {\n      try visitor.visitSingularBoolField(value: self.all, fieldNumber: 2)\n    }\n    if self.perms != 0 {\n      try visitor.visitSingularUInt32Field(value: self.perms, fieldNumber: 3)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_MkdirRequest, rhs: Com_Apple_Containerization_Sandbox_V3_MkdirRequest) -> Bool {\n    if lhs.path != rhs.path {return false}\n    if lhs.all != rhs.all {return false}\n    if lhs.perms != rhs.perms {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_MkdirResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".MkdirResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_MkdirResponse, rhs: Com_Apple_Containerization_Sandbox_V3_MkdirResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_WriteFileRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".WriteFileRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"path\"),\n    2: .same(proto: \"data\"),\n    3: .same(proto: \"mode\"),\n    4: .same(proto: \"flags\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.path) }()\n      case 2: try { try decoder.decodeSingularBytesField(value: &self.data) }()\n      case 3: try { try decoder.decodeSingularUInt32Field(value: &self.mode) }()\n      case 4: try { try decoder.decodeSingularMessageField(value: &self._flags) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    if !self.path.isEmpty {\n      try visitor.visitSingularStringField(value: self.path, fieldNumber: 1)\n    }\n    if !self.data.isEmpty {\n      try visitor.visitSingularBytesField(value: self.data, fieldNumber: 2)\n    }\n    if self.mode != 0 {\n      try visitor.visitSingularUInt32Field(value: self.mode, fieldNumber: 3)\n    }\n    try { if let v = self._flags {\n      try visitor.visitSingularMessageField(value: v, fieldNumber: 4)\n    } }()\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, rhs: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest) -> Bool {\n    if lhs.path != rhs.path {return false}\n    if lhs.data != rhs.data {return false}\n    if lhs.mode != rhs.mode {return false}\n    if lhs._flags != rhs._flags {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_WriteFileRequest.WriteFileFlags: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = Com_Apple_Containerization_Sandbox_V3_WriteFileRequest.protoMessageName + \".WriteFileFlags\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .standard(proto: \"create_parent_dirs\"),\n    2: .same(proto: \"append\"),\n    3: .standard(proto: \"create_if_missing\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularBoolField(value: &self.createParentDirs) }()\n      case 2: try { try decoder.decodeSingularBoolField(value: &self.append) }()\n      case 3: try { try decoder.decodeSingularBoolField(value: &self.createIfMissing) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if self.createParentDirs != false {\n      try visitor.visitSingularBoolField(value: self.createParentDirs, fieldNumber: 1)\n    }\n    if self.append != false {\n      try visitor.visitSingularBoolField(value: self.append, fieldNumber: 2)\n    }\n    if self.createIfMissing != false {\n      try visitor.visitSingularBoolField(value: self.createIfMissing, fieldNumber: 3)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest.WriteFileFlags, rhs: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest.WriteFileFlags) -> Bool {\n    if lhs.createParentDirs != rhs.createParentDirs {return false}\n    if lhs.append != rhs.append {return false}\n    if lhs.createIfMissing != rhs.createIfMissing {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_WriteFileResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".WriteFileResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_WriteFileResponse, rhs: Com_Apple_Containerization_Sandbox_V3_WriteFileResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_CopyRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".CopyRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"direction\"),\n    2: .same(proto: \"path\"),\n    3: .same(proto: \"mode\"),\n    4: .standard(proto: \"create_parents\"),\n    5: .standard(proto: \"vsock_port\"),\n    6: .standard(proto: \"is_archive\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularEnumField(value: &self.direction) }()\n      case 2: try { try decoder.decodeSingularStringField(value: &self.path) }()\n      case 3: try { try decoder.decodeSingularUInt32Field(value: &self.mode) }()\n      case 4: try { try decoder.decodeSingularBoolField(value: &self.createParents) }()\n      case 5: try { try decoder.decodeSingularUInt32Field(value: &self.vsockPort) }()\n      case 6: try { try decoder.decodeSingularBoolField(value: &self.isArchive) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if self.direction != .copyIn {\n      try visitor.visitSingularEnumField(value: self.direction, fieldNumber: 1)\n    }\n    if !self.path.isEmpty {\n      try visitor.visitSingularStringField(value: self.path, fieldNumber: 2)\n    }\n    if self.mode != 0 {\n      try visitor.visitSingularUInt32Field(value: self.mode, fieldNumber: 3)\n    }\n    if self.createParents != false {\n      try visitor.visitSingularBoolField(value: self.createParents, fieldNumber: 4)\n    }\n    if self.vsockPort != 0 {\n      try visitor.visitSingularUInt32Field(value: self.vsockPort, fieldNumber: 5)\n    }\n    if self.isArchive != false {\n      try visitor.visitSingularBoolField(value: self.isArchive, fieldNumber: 6)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_CopyRequest, rhs: Com_Apple_Containerization_Sandbox_V3_CopyRequest) -> Bool {\n    if lhs.direction != rhs.direction {return false}\n    if lhs.path != rhs.path {return false}\n    if lhs.mode != rhs.mode {return false}\n    if lhs.createParents != rhs.createParents {return false}\n    if lhs.vsockPort != rhs.vsockPort {return false}\n    if lhs.isArchive != rhs.isArchive {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_CopyRequest.Direction: SwiftProtobuf._ProtoNameProviding {\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    0: .same(proto: \"COPY_IN\"),\n    1: .same(proto: \"COPY_OUT\"),\n  ]\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_CopyResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".CopyResponse\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"status\"),\n    2: .standard(proto: \"is_archive\"),\n    3: .standard(proto: \"total_size\"),\n    4: .same(proto: \"error\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularEnumField(value: &self.status) }()\n      case 2: try { try decoder.decodeSingularBoolField(value: &self.isArchive) }()\n      case 3: try { try decoder.decodeSingularUInt64Field(value: &self.totalSize) }()\n      case 4: try { try decoder.decodeSingularStringField(value: &self.error) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if self.status != .metadata {\n      try visitor.visitSingularEnumField(value: self.status, fieldNumber: 1)\n    }\n    if self.isArchive != false {\n      try visitor.visitSingularBoolField(value: self.isArchive, fieldNumber: 2)\n    }\n    if self.totalSize != 0 {\n      try visitor.visitSingularUInt64Field(value: self.totalSize, fieldNumber: 3)\n    }\n    if !self.error.isEmpty {\n      try visitor.visitSingularStringField(value: self.error, fieldNumber: 4)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_CopyResponse, rhs: Com_Apple_Containerization_Sandbox_V3_CopyResponse) -> Bool {\n    if lhs.status != rhs.status {return false}\n    if lhs.isArchive != rhs.isArchive {return false}\n    if lhs.totalSize != rhs.totalSize {return false}\n    if lhs.error != rhs.error {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_CopyResponse.Status: SwiftProtobuf._ProtoNameProviding {\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    0: .same(proto: \"METADATA\"),\n    1: .same(proto: \"COMPLETE\"),\n  ]\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".IpLinkSetRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"interface\"),\n    2: .same(proto: \"up\"),\n    3: .same(proto: \"mtu\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.interface) }()\n      case 2: try { try decoder.decodeSingularBoolField(value: &self.up) }()\n      case 3: try { try decoder.decodeSingularUInt32Field(value: &self._mtu) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    if !self.interface.isEmpty {\n      try visitor.visitSingularStringField(value: self.interface, fieldNumber: 1)\n    }\n    if self.up != false {\n      try visitor.visitSingularBoolField(value: self.up, fieldNumber: 2)\n    }\n    try { if let v = self._mtu {\n      try visitor.visitSingularUInt32Field(value: v, fieldNumber: 3)\n    } }()\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest, rhs: Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest) -> Bool {\n    if lhs.interface != rhs.interface {return false}\n    if lhs.up != rhs.up {return false}\n    if lhs._mtu != rhs._mtu {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".IpLinkSetResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse, rhs: Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".IpAddrAddRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"interface\"),\n    2: .same(proto: \"ipv4Address\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.interface) }()\n      case 2: try { try decoder.decodeSingularStringField(value: &self.ipv4Address) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if !self.interface.isEmpty {\n      try visitor.visitSingularStringField(value: self.interface, fieldNumber: 1)\n    }\n    if !self.ipv4Address.isEmpty {\n      try visitor.visitSingularStringField(value: self.ipv4Address, fieldNumber: 2)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest, rhs: Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest) -> Bool {\n    if lhs.interface != rhs.interface {return false}\n    if lhs.ipv4Address != rhs.ipv4Address {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".IpAddrAddResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse, rhs: Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".IpRouteAddLinkRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"interface\"),\n    2: .same(proto: \"dstIpv4Addr\"),\n    3: .same(proto: \"srcIpv4Addr\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.interface) }()\n      case 2: try { try decoder.decodeSingularStringField(value: &self.dstIpv4Addr) }()\n      case 3: try { try decoder.decodeSingularStringField(value: &self.srcIpv4Addr) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if !self.interface.isEmpty {\n      try visitor.visitSingularStringField(value: self.interface, fieldNumber: 1)\n    }\n    if !self.dstIpv4Addr.isEmpty {\n      try visitor.visitSingularStringField(value: self.dstIpv4Addr, fieldNumber: 2)\n    }\n    if !self.srcIpv4Addr.isEmpty {\n      try visitor.visitSingularStringField(value: self.srcIpv4Addr, fieldNumber: 3)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest, rhs: Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest) -> Bool {\n    if lhs.interface != rhs.interface {return false}\n    if lhs.dstIpv4Addr != rhs.dstIpv4Addr {return false}\n    if lhs.srcIpv4Addr != rhs.srcIpv4Addr {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".IpRouteAddLinkResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse, rhs: Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".IpRouteAddDefaultRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"interface\"),\n    2: .same(proto: \"ipv4Gateway\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.interface) }()\n      case 2: try { try decoder.decodeSingularStringField(value: &self.ipv4Gateway) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if !self.interface.isEmpty {\n      try visitor.visitSingularStringField(value: self.interface, fieldNumber: 1)\n    }\n    if !self.ipv4Gateway.isEmpty {\n      try visitor.visitSingularStringField(value: self.ipv4Gateway, fieldNumber: 2)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest, rhs: Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest) -> Bool {\n    if lhs.interface != rhs.interface {return false}\n    if lhs.ipv4Gateway != rhs.ipv4Gateway {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".IpRouteAddDefaultResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse, rhs: Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".ConfigureDnsRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"location\"),\n    2: .same(proto: \"nameservers\"),\n    3: .same(proto: \"domain\"),\n    4: .same(proto: \"searchDomains\"),\n    5: .same(proto: \"options\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.location) }()\n      case 2: try { try decoder.decodeRepeatedStringField(value: &self.nameservers) }()\n      case 3: try { try decoder.decodeSingularStringField(value: &self._domain) }()\n      case 4: try { try decoder.decodeRepeatedStringField(value: &self.searchDomains) }()\n      case 5: try { try decoder.decodeRepeatedStringField(value: &self.options) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    if !self.location.isEmpty {\n      try visitor.visitSingularStringField(value: self.location, fieldNumber: 1)\n    }\n    if !self.nameservers.isEmpty {\n      try visitor.visitRepeatedStringField(value: self.nameservers, fieldNumber: 2)\n    }\n    try { if let v = self._domain {\n      try visitor.visitSingularStringField(value: v, fieldNumber: 3)\n    } }()\n    if !self.searchDomains.isEmpty {\n      try visitor.visitRepeatedStringField(value: self.searchDomains, fieldNumber: 4)\n    }\n    if !self.options.isEmpty {\n      try visitor.visitRepeatedStringField(value: self.options, fieldNumber: 5)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest, rhs: Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest) -> Bool {\n    if lhs.location != rhs.location {return false}\n    if lhs.nameservers != rhs.nameservers {return false}\n    if lhs._domain != rhs._domain {return false}\n    if lhs.searchDomains != rhs.searchDomains {return false}\n    if lhs.options != rhs.options {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".ConfigureDnsResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse, rhs: Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".ConfigureHostsRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"location\"),\n    2: .same(proto: \"entries\"),\n    3: .same(proto: \"comment\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.location) }()\n      case 2: try { try decoder.decodeRepeatedMessageField(value: &self.entries) }()\n      case 3: try { try decoder.decodeSingularStringField(value: &self._comment) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    if !self.location.isEmpty {\n      try visitor.visitSingularStringField(value: self.location, fieldNumber: 1)\n    }\n    if !self.entries.isEmpty {\n      try visitor.visitRepeatedMessageField(value: self.entries, fieldNumber: 2)\n    }\n    try { if let v = self._comment {\n      try visitor.visitSingularStringField(value: v, fieldNumber: 3)\n    } }()\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest, rhs: Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest) -> Bool {\n    if lhs.location != rhs.location {return false}\n    if lhs.entries != rhs.entries {return false}\n    if lhs._comment != rhs._comment {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest.HostsEntry: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest.protoMessageName + \".HostsEntry\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"ipAddress\"),\n    2: .same(proto: \"hostnames\"),\n    3: .same(proto: \"comment\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.ipAddress) }()\n      case 2: try { try decoder.decodeRepeatedStringField(value: &self.hostnames) }()\n      case 3: try { try decoder.decodeSingularStringField(value: &self._comment) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    // The use of inline closures is to circumvent an issue where the compiler\n    // allocates stack space for every if/case branch local when no optimizations\n    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n    // https://github.com/apple/swift-protobuf/issues/1182\n    if !self.ipAddress.isEmpty {\n      try visitor.visitSingularStringField(value: self.ipAddress, fieldNumber: 1)\n    }\n    if !self.hostnames.isEmpty {\n      try visitor.visitRepeatedStringField(value: self.hostnames, fieldNumber: 2)\n    }\n    try { if let v = self._comment {\n      try visitor.visitSingularStringField(value: v, fieldNumber: 3)\n    } }()\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest.HostsEntry, rhs: Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest.HostsEntry) -> Bool {\n    if lhs.ipAddress != rhs.ipAddress {return false}\n    if lhs.hostnames != rhs.hostnames {return false}\n    if lhs._comment != rhs._comment {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".ConfigureHostsResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse, rhs: Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_SyncRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".SyncRequest\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_SyncRequest, rhs: Com_Apple_Containerization_Sandbox_V3_SyncRequest) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_SyncResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".SyncResponse\"\n  public static let _protobuf_nameMap = SwiftProtobuf._NameMap()\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    // Load everything into unknown fields\n    while try decoder.nextFieldNumber() != nil {}\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_SyncResponse, rhs: Com_Apple_Containerization_Sandbox_V3_SyncResponse) -> Bool {\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_KillRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".KillRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"pid\"),\n    3: .same(proto: \"signal\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularInt32Field(value: &self.pid) }()\n      case 3: try { try decoder.decodeSingularInt32Field(value: &self.signal) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if self.pid != 0 {\n      try visitor.visitSingularInt32Field(value: self.pid, fieldNumber: 1)\n    }\n    if self.signal != 0 {\n      try visitor.visitSingularInt32Field(value: self.signal, fieldNumber: 3)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_KillRequest, rhs: Com_Apple_Containerization_Sandbox_V3_KillRequest) -> Bool {\n    if lhs.pid != rhs.pid {return false}\n    if lhs.signal != rhs.signal {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_KillResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".KillResponse\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"result\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularInt32Field(value: &self.result) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if self.result != 0 {\n      try visitor.visitSingularInt32Field(value: self.result, fieldNumber: 1)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_KillResponse, rhs: Com_Apple_Containerization_Sandbox_V3_KillResponse) -> Bool {\n    if lhs.result != rhs.result {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".ContainerStatisticsRequest\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .standard(proto: \"container_ids\"),\n    2: .same(proto: \"categories\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeRepeatedStringField(value: &self.containerIds) }()\n      case 2: try { try decoder.decodeRepeatedEnumField(value: &self.categories) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if !self.containerIds.isEmpty {\n      try visitor.visitRepeatedStringField(value: self.containerIds, fieldNumber: 1)\n    }\n    if !self.categories.isEmpty {\n      try visitor.visitPackedEnumField(value: self.categories, fieldNumber: 2)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest, rhs: Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest) -> Bool {\n    if lhs.containerIds != rhs.containerIds {return false}\n    if lhs.categories != rhs.categories {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".ContainerStatisticsResponse\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"containers\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeRepeatedMessageField(value: &self.containers) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if !self.containers.isEmpty {\n      try visitor.visitRepeatedMessageField(value: self.containers, fieldNumber: 1)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse, rhs: Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse) -> Bool {\n    if lhs.containers != rhs.containers {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_ContainerStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".ContainerStats\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .standard(proto: \"container_id\"),\n    2: .same(proto: \"process\"),\n    3: .same(proto: \"memory\"),\n    4: .same(proto: \"cpu\"),\n    5: .standard(proto: \"block_io\"),\n    6: .same(proto: \"networks\"),\n    7: .standard(proto: \"memory_events\"),\n  ]\n\n  fileprivate class _StorageClass {\n    var _containerID: String = String()\n    var _process: Com_Apple_Containerization_Sandbox_V3_ProcessStats? = nil\n    var _memory: Com_Apple_Containerization_Sandbox_V3_MemoryStats? = nil\n    var _cpu: Com_Apple_Containerization_Sandbox_V3_CPUStats? = nil\n    var _blockIo: Com_Apple_Containerization_Sandbox_V3_BlockIOStats? = nil\n    var _networks: [Com_Apple_Containerization_Sandbox_V3_NetworkStats] = []\n    var _memoryEvents: Com_Apple_Containerization_Sandbox_V3_MemoryEventStats? = nil\n\n      // This property is used as the initial default value for new instances of the type.\n      // The type itself is protecting the reference to its storage via CoW semantics.\n      // This will force a copy to be made of this reference when the first mutation occurs;\n      // hence, it is safe to mark this as `nonisolated(unsafe)`.\n      static nonisolated(unsafe) let defaultInstance = _StorageClass()\n\n    private init() {}\n\n    init(copying source: _StorageClass) {\n      _containerID = source._containerID\n      _process = source._process\n      _memory = source._memory\n      _cpu = source._cpu\n      _blockIo = source._blockIo\n      _networks = source._networks\n      _memoryEvents = source._memoryEvents\n    }\n  }\n\n  fileprivate mutating func _uniqueStorage() -> _StorageClass {\n    if !isKnownUniquelyReferenced(&_storage) {\n      _storage = _StorageClass(copying: _storage)\n    }\n    return _storage\n  }\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    _ = _uniqueStorage()\n    try withExtendedLifetime(_storage) { (_storage: _StorageClass) in\n      while let fieldNumber = try decoder.nextFieldNumber() {\n        // The use of inline closures is to circumvent an issue where the compiler\n        // allocates stack space for every case branch when no optimizations are\n        // enabled. https://github.com/apple/swift-protobuf/issues/1034\n        switch fieldNumber {\n        case 1: try { try decoder.decodeSingularStringField(value: &_storage._containerID) }()\n        case 2: try { try decoder.decodeSingularMessageField(value: &_storage._process) }()\n        case 3: try { try decoder.decodeSingularMessageField(value: &_storage._memory) }()\n        case 4: try { try decoder.decodeSingularMessageField(value: &_storage._cpu) }()\n        case 5: try { try decoder.decodeSingularMessageField(value: &_storage._blockIo) }()\n        case 6: try { try decoder.decodeRepeatedMessageField(value: &_storage._networks) }()\n        case 7: try { try decoder.decodeSingularMessageField(value: &_storage._memoryEvents) }()\n        default: break\n        }\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    try withExtendedLifetime(_storage) { (_storage: _StorageClass) in\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every if/case branch local when no optimizations\n      // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and\n      // https://github.com/apple/swift-protobuf/issues/1182\n      if !_storage._containerID.isEmpty {\n        try visitor.visitSingularStringField(value: _storage._containerID, fieldNumber: 1)\n      }\n      try { if let v = _storage._process {\n        try visitor.visitSingularMessageField(value: v, fieldNumber: 2)\n      } }()\n      try { if let v = _storage._memory {\n        try visitor.visitSingularMessageField(value: v, fieldNumber: 3)\n      } }()\n      try { if let v = _storage._cpu {\n        try visitor.visitSingularMessageField(value: v, fieldNumber: 4)\n      } }()\n      try { if let v = _storage._blockIo {\n        try visitor.visitSingularMessageField(value: v, fieldNumber: 5)\n      } }()\n      if !_storage._networks.isEmpty {\n        try visitor.visitRepeatedMessageField(value: _storage._networks, fieldNumber: 6)\n      }\n      try { if let v = _storage._memoryEvents {\n        try visitor.visitSingularMessageField(value: v, fieldNumber: 7)\n      } }()\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_ContainerStats, rhs: Com_Apple_Containerization_Sandbox_V3_ContainerStats) -> Bool {\n    if lhs._storage !== rhs._storage {\n      let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in\n        let _storage = _args.0\n        let rhs_storage = _args.1\n        if _storage._containerID != rhs_storage._containerID {return false}\n        if _storage._process != rhs_storage._process {return false}\n        if _storage._memory != rhs_storage._memory {return false}\n        if _storage._cpu != rhs_storage._cpu {return false}\n        if _storage._blockIo != rhs_storage._blockIo {return false}\n        if _storage._networks != rhs_storage._networks {return false}\n        if _storage._memoryEvents != rhs_storage._memoryEvents {return false}\n        return true\n      }\n      if !storagesAreEqual {return false}\n    }\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_ProcessStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".ProcessStats\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"current\"),\n    2: .same(proto: \"limit\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularUInt64Field(value: &self.current) }()\n      case 2: try { try decoder.decodeSingularUInt64Field(value: &self.limit) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if self.current != 0 {\n      try visitor.visitSingularUInt64Field(value: self.current, fieldNumber: 1)\n    }\n    if self.limit != 0 {\n      try visitor.visitSingularUInt64Field(value: self.limit, fieldNumber: 2)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_ProcessStats, rhs: Com_Apple_Containerization_Sandbox_V3_ProcessStats) -> Bool {\n    if lhs.current != rhs.current {return false}\n    if lhs.limit != rhs.limit {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_MemoryStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".MemoryStats\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .standard(proto: \"usage_bytes\"),\n    2: .standard(proto: \"limit_bytes\"),\n    3: .standard(proto: \"swap_usage_bytes\"),\n    4: .standard(proto: \"swap_limit_bytes\"),\n    5: .standard(proto: \"cache_bytes\"),\n    6: .standard(proto: \"kernel_stack_bytes\"),\n    7: .standard(proto: \"slab_bytes\"),\n    8: .standard(proto: \"page_faults\"),\n    9: .standard(proto: \"major_page_faults\"),\n    10: .standard(proto: \"inactive_file\"),\n    11: .same(proto: \"anon\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularUInt64Field(value: &self.usageBytes) }()\n      case 2: try { try decoder.decodeSingularUInt64Field(value: &self.limitBytes) }()\n      case 3: try { try decoder.decodeSingularUInt64Field(value: &self.swapUsageBytes) }()\n      case 4: try { try decoder.decodeSingularUInt64Field(value: &self.swapLimitBytes) }()\n      case 5: try { try decoder.decodeSingularUInt64Field(value: &self.cacheBytes) }()\n      case 6: try { try decoder.decodeSingularUInt64Field(value: &self.kernelStackBytes) }()\n      case 7: try { try decoder.decodeSingularUInt64Field(value: &self.slabBytes) }()\n      case 8: try { try decoder.decodeSingularUInt64Field(value: &self.pageFaults) }()\n      case 9: try { try decoder.decodeSingularUInt64Field(value: &self.majorPageFaults) }()\n      case 10: try { try decoder.decodeSingularUInt64Field(value: &self.inactiveFile) }()\n      case 11: try { try decoder.decodeSingularUInt64Field(value: &self.anon) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if self.usageBytes != 0 {\n      try visitor.visitSingularUInt64Field(value: self.usageBytes, fieldNumber: 1)\n    }\n    if self.limitBytes != 0 {\n      try visitor.visitSingularUInt64Field(value: self.limitBytes, fieldNumber: 2)\n    }\n    if self.swapUsageBytes != 0 {\n      try visitor.visitSingularUInt64Field(value: self.swapUsageBytes, fieldNumber: 3)\n    }\n    if self.swapLimitBytes != 0 {\n      try visitor.visitSingularUInt64Field(value: self.swapLimitBytes, fieldNumber: 4)\n    }\n    if self.cacheBytes != 0 {\n      try visitor.visitSingularUInt64Field(value: self.cacheBytes, fieldNumber: 5)\n    }\n    if self.kernelStackBytes != 0 {\n      try visitor.visitSingularUInt64Field(value: self.kernelStackBytes, fieldNumber: 6)\n    }\n    if self.slabBytes != 0 {\n      try visitor.visitSingularUInt64Field(value: self.slabBytes, fieldNumber: 7)\n    }\n    if self.pageFaults != 0 {\n      try visitor.visitSingularUInt64Field(value: self.pageFaults, fieldNumber: 8)\n    }\n    if self.majorPageFaults != 0 {\n      try visitor.visitSingularUInt64Field(value: self.majorPageFaults, fieldNumber: 9)\n    }\n    if self.inactiveFile != 0 {\n      try visitor.visitSingularUInt64Field(value: self.inactiveFile, fieldNumber: 10)\n    }\n    if self.anon != 0 {\n      try visitor.visitSingularUInt64Field(value: self.anon, fieldNumber: 11)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_MemoryStats, rhs: Com_Apple_Containerization_Sandbox_V3_MemoryStats) -> Bool {\n    if lhs.usageBytes != rhs.usageBytes {return false}\n    if lhs.limitBytes != rhs.limitBytes {return false}\n    if lhs.swapUsageBytes != rhs.swapUsageBytes {return false}\n    if lhs.swapLimitBytes != rhs.swapLimitBytes {return false}\n    if lhs.cacheBytes != rhs.cacheBytes {return false}\n    if lhs.kernelStackBytes != rhs.kernelStackBytes {return false}\n    if lhs.slabBytes != rhs.slabBytes {return false}\n    if lhs.pageFaults != rhs.pageFaults {return false}\n    if lhs.majorPageFaults != rhs.majorPageFaults {return false}\n    if lhs.inactiveFile != rhs.inactiveFile {return false}\n    if lhs.anon != rhs.anon {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_CPUStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".CPUStats\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .standard(proto: \"usage_usec\"),\n    2: .standard(proto: \"user_usec\"),\n    3: .standard(proto: \"system_usec\"),\n    4: .standard(proto: \"throttling_periods\"),\n    5: .standard(proto: \"throttled_periods\"),\n    6: .standard(proto: \"throttled_time_usec\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularUInt64Field(value: &self.usageUsec) }()\n      case 2: try { try decoder.decodeSingularUInt64Field(value: &self.userUsec) }()\n      case 3: try { try decoder.decodeSingularUInt64Field(value: &self.systemUsec) }()\n      case 4: try { try decoder.decodeSingularUInt64Field(value: &self.throttlingPeriods) }()\n      case 5: try { try decoder.decodeSingularUInt64Field(value: &self.throttledPeriods) }()\n      case 6: try { try decoder.decodeSingularUInt64Field(value: &self.throttledTimeUsec) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if self.usageUsec != 0 {\n      try visitor.visitSingularUInt64Field(value: self.usageUsec, fieldNumber: 1)\n    }\n    if self.userUsec != 0 {\n      try visitor.visitSingularUInt64Field(value: self.userUsec, fieldNumber: 2)\n    }\n    if self.systemUsec != 0 {\n      try visitor.visitSingularUInt64Field(value: self.systemUsec, fieldNumber: 3)\n    }\n    if self.throttlingPeriods != 0 {\n      try visitor.visitSingularUInt64Field(value: self.throttlingPeriods, fieldNumber: 4)\n    }\n    if self.throttledPeriods != 0 {\n      try visitor.visitSingularUInt64Field(value: self.throttledPeriods, fieldNumber: 5)\n    }\n    if self.throttledTimeUsec != 0 {\n      try visitor.visitSingularUInt64Field(value: self.throttledTimeUsec, fieldNumber: 6)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_CPUStats, rhs: Com_Apple_Containerization_Sandbox_V3_CPUStats) -> Bool {\n    if lhs.usageUsec != rhs.usageUsec {return false}\n    if lhs.userUsec != rhs.userUsec {return false}\n    if lhs.systemUsec != rhs.systemUsec {return false}\n    if lhs.throttlingPeriods != rhs.throttlingPeriods {return false}\n    if lhs.throttledPeriods != rhs.throttledPeriods {return false}\n    if lhs.throttledTimeUsec != rhs.throttledTimeUsec {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_BlockIOStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".BlockIOStats\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"devices\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeRepeatedMessageField(value: &self.devices) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if !self.devices.isEmpty {\n      try visitor.visitRepeatedMessageField(value: self.devices, fieldNumber: 1)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_BlockIOStats, rhs: Com_Apple_Containerization_Sandbox_V3_BlockIOStats) -> Bool {\n    if lhs.devices != rhs.devices {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_BlockIOEntry: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".BlockIOEntry\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"major\"),\n    2: .same(proto: \"minor\"),\n    3: .standard(proto: \"read_bytes\"),\n    4: .standard(proto: \"write_bytes\"),\n    5: .standard(proto: \"read_operations\"),\n    6: .standard(proto: \"write_operations\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularUInt64Field(value: &self.major) }()\n      case 2: try { try decoder.decodeSingularUInt64Field(value: &self.minor) }()\n      case 3: try { try decoder.decodeSingularUInt64Field(value: &self.readBytes) }()\n      case 4: try { try decoder.decodeSingularUInt64Field(value: &self.writeBytes) }()\n      case 5: try { try decoder.decodeSingularUInt64Field(value: &self.readOperations) }()\n      case 6: try { try decoder.decodeSingularUInt64Field(value: &self.writeOperations) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if self.major != 0 {\n      try visitor.visitSingularUInt64Field(value: self.major, fieldNumber: 1)\n    }\n    if self.minor != 0 {\n      try visitor.visitSingularUInt64Field(value: self.minor, fieldNumber: 2)\n    }\n    if self.readBytes != 0 {\n      try visitor.visitSingularUInt64Field(value: self.readBytes, fieldNumber: 3)\n    }\n    if self.writeBytes != 0 {\n      try visitor.visitSingularUInt64Field(value: self.writeBytes, fieldNumber: 4)\n    }\n    if self.readOperations != 0 {\n      try visitor.visitSingularUInt64Field(value: self.readOperations, fieldNumber: 5)\n    }\n    if self.writeOperations != 0 {\n      try visitor.visitSingularUInt64Field(value: self.writeOperations, fieldNumber: 6)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_BlockIOEntry, rhs: Com_Apple_Containerization_Sandbox_V3_BlockIOEntry) -> Bool {\n    if lhs.major != rhs.major {return false}\n    if lhs.minor != rhs.minor {return false}\n    if lhs.readBytes != rhs.readBytes {return false}\n    if lhs.writeBytes != rhs.writeBytes {return false}\n    if lhs.readOperations != rhs.readOperations {return false}\n    if lhs.writeOperations != rhs.writeOperations {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_NetworkStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".NetworkStats\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"interface\"),\n    2: .same(proto: \"receivedPackets\"),\n    3: .same(proto: \"transmittedPackets\"),\n    4: .same(proto: \"receivedBytes\"),\n    5: .same(proto: \"transmittedBytes\"),\n    6: .same(proto: \"receivedErrors\"),\n    7: .same(proto: \"transmittedErrors\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularStringField(value: &self.interface) }()\n      case 2: try { try decoder.decodeSingularUInt64Field(value: &self.receivedPackets) }()\n      case 3: try { try decoder.decodeSingularUInt64Field(value: &self.transmittedPackets) }()\n      case 4: try { try decoder.decodeSingularUInt64Field(value: &self.receivedBytes) }()\n      case 5: try { try decoder.decodeSingularUInt64Field(value: &self.transmittedBytes) }()\n      case 6: try { try decoder.decodeSingularUInt64Field(value: &self.receivedErrors) }()\n      case 7: try { try decoder.decodeSingularUInt64Field(value: &self.transmittedErrors) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if !self.interface.isEmpty {\n      try visitor.visitSingularStringField(value: self.interface, fieldNumber: 1)\n    }\n    if self.receivedPackets != 0 {\n      try visitor.visitSingularUInt64Field(value: self.receivedPackets, fieldNumber: 2)\n    }\n    if self.transmittedPackets != 0 {\n      try visitor.visitSingularUInt64Field(value: self.transmittedPackets, fieldNumber: 3)\n    }\n    if self.receivedBytes != 0 {\n      try visitor.visitSingularUInt64Field(value: self.receivedBytes, fieldNumber: 4)\n    }\n    if self.transmittedBytes != 0 {\n      try visitor.visitSingularUInt64Field(value: self.transmittedBytes, fieldNumber: 5)\n    }\n    if self.receivedErrors != 0 {\n      try visitor.visitSingularUInt64Field(value: self.receivedErrors, fieldNumber: 6)\n    }\n    if self.transmittedErrors != 0 {\n      try visitor.visitSingularUInt64Field(value: self.transmittedErrors, fieldNumber: 7)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_NetworkStats, rhs: Com_Apple_Containerization_Sandbox_V3_NetworkStats) -> Bool {\n    if lhs.interface != rhs.interface {return false}\n    if lhs.receivedPackets != rhs.receivedPackets {return false}\n    if lhs.transmittedPackets != rhs.transmittedPackets {return false}\n    if lhs.receivedBytes != rhs.receivedBytes {return false}\n    if lhs.transmittedBytes != rhs.transmittedBytes {return false}\n    if lhs.receivedErrors != rhs.receivedErrors {return false}\n    if lhs.transmittedErrors != rhs.transmittedErrors {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_MemoryEventStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {\n  public static let protoMessageName: String = _protobuf_package + \".MemoryEventStats\"\n  public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [\n    1: .same(proto: \"low\"),\n    2: .same(proto: \"high\"),\n    3: .same(proto: \"max\"),\n    4: .same(proto: \"oom\"),\n    5: .standard(proto: \"oom_kill\"),\n    6: .standard(proto: \"oom_group_kill\"),\n  ]\n\n  public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {\n    while let fieldNumber = try decoder.nextFieldNumber() {\n      // The use of inline closures is to circumvent an issue where the compiler\n      // allocates stack space for every case branch when no optimizations are\n      // enabled. https://github.com/apple/swift-protobuf/issues/1034\n      switch fieldNumber {\n      case 1: try { try decoder.decodeSingularUInt64Field(value: &self.low) }()\n      case 2: try { try decoder.decodeSingularUInt64Field(value: &self.high) }()\n      case 3: try { try decoder.decodeSingularUInt64Field(value: &self.max) }()\n      case 4: try { try decoder.decodeSingularUInt64Field(value: &self.oom) }()\n      case 5: try { try decoder.decodeSingularUInt64Field(value: &self.oomKill) }()\n      case 6: try { try decoder.decodeSingularUInt64Field(value: &self.oomGroupKill) }()\n      default: break\n      }\n    }\n  }\n\n  public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {\n    if self.low != 0 {\n      try visitor.visitSingularUInt64Field(value: self.low, fieldNumber: 1)\n    }\n    if self.high != 0 {\n      try visitor.visitSingularUInt64Field(value: self.high, fieldNumber: 2)\n    }\n    if self.max != 0 {\n      try visitor.visitSingularUInt64Field(value: self.max, fieldNumber: 3)\n    }\n    if self.oom != 0 {\n      try visitor.visitSingularUInt64Field(value: self.oom, fieldNumber: 4)\n    }\n    if self.oomKill != 0 {\n      try visitor.visitSingularUInt64Field(value: self.oomKill, fieldNumber: 5)\n    }\n    if self.oomGroupKill != 0 {\n      try visitor.visitSingularUInt64Field(value: self.oomGroupKill, fieldNumber: 6)\n    }\n    try unknownFields.traverse(visitor: &visitor)\n  }\n\n  public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_MemoryEventStats, rhs: Com_Apple_Containerization_Sandbox_V3_MemoryEventStats) -> Bool {\n    if lhs.low != rhs.low {return false}\n    if lhs.high != rhs.high {return false}\n    if lhs.max != rhs.max {return false}\n    if lhs.oom != rhs.oom {return false}\n    if lhs.oomKill != rhs.oomKill {return false}\n    if lhs.oomGroupKill != rhs.oomGroupKill {return false}\n    if lhs.unknownFields != rhs.unknownFields {return false}\n    return true\n  }\n}\n"
  },
  {
    "path": "Sources/Containerization/SandboxContext/SandboxContext.proto",
    "content": "syntax = \"proto3\";\n\npackage com.apple.containerization.sandbox.v3;\n\nimport \"google/protobuf/timestamp.proto\";\n\n// Context for interacting with a container's runtime environment.\nservice SandboxContext {\n  // Mount a filesystem.\n  rpc Mount(MountRequest) returns (MountResponse);\n  // Unmount a filesystem.\n  rpc Umount(UmountRequest) returns (UmountResponse);\n  // Set an environment variable on the init process.\n  rpc Setenv(SetenvRequest) returns (SetenvResponse);\n  // Get an environment variable from the init process.\n  rpc Getenv(GetenvRequest) returns (GetenvResponse);\n  // Create a new directory inside the sandbox.\n  rpc Mkdir(MkdirRequest) returns (MkdirResponse);\n  // Set sysctls in the context of the sandbox.\n  rpc Sysctl(SysctlRequest) returns (SysctlResponse);\n  // Set time in the guest.\n  rpc SetTime(SetTimeRequest) returns (SetTimeResponse);\n  // Set up an emulator in the guest for a specific binary format.\n  rpc SetupEmulator(SetupEmulatorRequest) returns (SetupEmulatorResponse);\n  // Write data to an existing or new file.\n  rpc WriteFile(WriteFileRequest) returns (WriteFileResponse);\n  // Copy a file or directory between the host and guest.\n  // Data transfer happens over a dedicated vsock connection;\n  // the gRPC stream is used only for control/metadata.\n  rpc Copy(CopyRequest) returns (stream CopyResponse);\n\n  // Create a new process inside the container.\n  rpc CreateProcess(CreateProcessRequest) returns (CreateProcessResponse);\n  // Delete an existing process inside the container.\n  rpc DeleteProcess(DeleteProcessRequest) returns (DeleteProcessResponse);\n  // Start the provided process.\n  rpc StartProcess(StartProcessRequest) returns (StartProcessResponse);\n  // Send a signal to the provided process.\n  rpc KillProcess(KillProcessRequest) returns (KillProcessResponse);\n  // Wait for a process to exit and return the exit code.\n  rpc WaitProcess(WaitProcessRequest) returns (WaitProcessResponse);\n  // Resize the tty of a given process. This will error if the process does\n  // not have a pty allocated.\n  rpc ResizeProcess(ResizeProcessRequest) returns (ResizeProcessResponse);\n  // Close IO for a given process.\n  rpc CloseProcessStdin(CloseProcessStdinRequest) returns (CloseProcessStdinResponse);\n\n  // Get statistics for containers.\n  rpc ContainerStatistics(ContainerStatisticsRequest) returns (ContainerStatisticsResponse);\n\n  // Proxy a vsock port to a unix domain socket in the guest, or vice versa.\n  rpc ProxyVsock(ProxyVsockRequest) returns (ProxyVsockResponse);\n  // Stop a vsock proxy to a unix domain socket.\n  rpc StopVsockProxy(StopVsockProxyRequest) returns (StopVsockProxyResponse);\n\n  // Set the link state of a network interface.\n  rpc IpLinkSet(IpLinkSetRequest) returns (IpLinkSetResponse);\n  // Add an IPv4 address to a network interface.\n  rpc IpAddrAdd(IpAddrAddRequest) returns (IpAddrAddResponse);\n  // Add an IP route for a network interface.\n  rpc IpRouteAddLink(IpRouteAddLinkRequest) returns (IpRouteAddLinkResponse);\n  // Add an IP route for a network interface.\n  rpc IpRouteAddDefault(IpRouteAddDefaultRequest) returns (IpRouteAddDefaultResponse);\n  // Configure DNS resolver.\n  rpc ConfigureDns(ConfigureDnsRequest) returns (ConfigureDnsResponse);\n  // Configure /etc/hosts.\n  rpc ConfigureHosts(ConfigureHostsRequest) returns (ConfigureHostsResponse);\n\n  // Perform the sync syscall.\n  rpc Sync(SyncRequest) returns (SyncResponse);\n  // Send a signal to a process via the PID.\n  rpc Kill(KillRequest) returns (KillResponse);\n}\n\nmessage Stdio {\n  optional int32 stdinPort = 1;\n  optional int32 stdoutPort = 2;\n  optional int32 stderrPort = 3;\n}\n\nmessage SetupEmulatorRequest {\n  string binary_path = 1;\n  string name = 2;\n  string type = 3;\n  string offset = 4;\n  string magic = 5;\n  string mask = 6;\n  string flags = 7;\n}\n\nmessage SetupEmulatorResponse {}\n\nmessage SetTimeRequest {\n  int64 sec = 1;\n  int32 usec = 2;\n}\n\nmessage SetTimeResponse {}\n\nmessage SysctlRequest { map<string, string> settings = 1; }\n\nmessage SysctlResponse {}\n\nmessage ProxyVsockRequest {\n  enum Action {\n    INTO = 0;\n    OUT_OF = 1;\n  }\n  string id = 1;\n  uint32 vsock_port = 2;\n  string guestPath = 3;\n  optional uint32 guestSocketPermissions = 4;\n  Action action = 5;\n}\n\nmessage ProxyVsockResponse {}\n\nmessage StopVsockProxyRequest { string id = 1; }\n\nmessage StopVsockProxyResponse {}\n\nmessage MountRequest {\n  string type = 1;\n  string source = 2;\n  string destination = 3;\n  repeated string options = 4;\n}\n\nmessage MountResponse {}\n\nmessage UmountRequest {\n  string path = 1;\n  int32 flags = 2;\n}\n\nmessage UmountResponse {}\n\nmessage SetenvRequest {\n  string key = 1;\n  optional string value = 2;\n}\n\nmessage SetenvResponse {}\n\nmessage GetenvRequest { string key = 1; }\n\nmessage GetenvResponse { optional string value = 1; }\n\nmessage CreateProcessRequest {\n  string id = 1;\n  optional string containerID = 2;\n  optional uint32 stdin = 3;\n  optional uint32 stdout = 4;\n  optional uint32 stderr = 5;\n  optional string ociRuntimePath = 6;\n  bytes configuration = 7;\n  optional bytes options = 8;\n}\n\nmessage CreateProcessResponse {}\n\nmessage WaitProcessRequest {\n  string id = 1;\n  optional string containerID = 2;\n}\n\nmessage WaitProcessResponse {\n  int32 exitCode = 1;\n  google.protobuf.Timestamp exited_at = 2;\n}\n\nmessage ResizeProcessRequest {\n  string id = 1;\n  optional string containerID = 2;\n  uint32 rows = 3;\n  uint32 columns = 4;\n}\n\nmessage ResizeProcessResponse {}\n\nmessage DeleteProcessRequest {\n  string id = 1;\n  optional string containerID = 2;\n}\n\nmessage DeleteProcessResponse {}\n\nmessage StartProcessRequest {\n  string id = 1;\n  optional string containerID = 2;\n}\n\nmessage StartProcessResponse { int32 pid = 1; }\n\nmessage KillProcessRequest {\n  string id = 1;\n  optional string containerID = 2;\n  int32 signal = 3;\n}\n\nmessage KillProcessResponse { int32 result = 1; }\n\nmessage CloseProcessStdinRequest {\n  string id = 1;\n  optional string containerID = 2;\n}\n\nmessage CloseProcessStdinResponse {}\n\nmessage MkdirRequest {\n  string path = 1;\n  bool all = 2;\n  uint32 perms = 3;\n}\n\nmessage MkdirResponse {}\n\nmessage WriteFileRequest {\n  message WriteFileFlags {\n    bool create_parent_dirs = 1;\n    bool append = 2;\n    bool create_if_missing = 3;\n  }\n  string path = 1;\n  bytes data = 2;\n  uint32 mode = 3;\n  WriteFileFlags flags = 4;\n}\n\nmessage WriteFileResponse {}\n\nmessage CopyRequest {\n  enum Direction {\n    // Copy from host into guest.\n    COPY_IN = 0;\n    // Copy from guest to host.\n    COPY_OUT = 1;\n  }\n  // Direction of the copy operation.\n  Direction direction = 1;\n  // Path in the guest (destination for COPY_IN, source for COPY_OUT).\n  string path = 2;\n  // File mode for single-file COPY_IN (defaults to 0644 if not set).\n  uint32 mode = 3;\n  // Create parent directories if they don't exist.\n  bool create_parents = 4;\n  // Vsock port the host is listening on for data transfer.\n  uint32 vsock_port = 5;\n  // For COPY_IN: indicates the data arriving on vsock is a tar+gzip archive.\n  bool is_archive = 6;\n}\n\nmessage CopyResponse {\n  enum Status {\n    // Transfer metadata (first message for COPY_OUT: is_archive, total_size).\n    METADATA = 0;\n    // Data transfer completed successfully.\n    COMPLETE = 1;\n  }\n  // What this response represents.\n  Status status = 1;\n  // For COPY_OUT METADATA: indicates the data on vsock will be a tar+gzip archive.\n  bool is_archive = 2;\n  // For COPY_OUT METADATA: total size in bytes (0 if unknown, e.g. for archives).\n  uint64 total_size = 3;\n  // Non-empty if an error occurred.\n  string error = 4;\n}\n\nmessage IpLinkSetRequest {\n  string interface = 1;\n  bool up = 2;\n  optional uint32 mtu = 3;\n}\n\nmessage IpLinkSetResponse {}\n\nmessage IpAddrAddRequest {\n  string interface = 1;\n  string ipv4Address = 2;\n}\n\nmessage IpAddrAddResponse {}\n\nmessage IpRouteAddLinkRequest {\n  string interface = 1;\n  string dstIpv4Addr = 2;\n  string srcIpv4Addr = 3;\n}\n\nmessage IpRouteAddLinkResponse {}\n\nmessage IpRouteAddDefaultRequest {\n  string interface = 1;\n  string ipv4Gateway = 2;\n}\n\nmessage IpRouteAddDefaultResponse {}\n\nmessage ConfigureDnsRequest {\n  string location = 1;\n  repeated string nameservers = 2;\n  optional string domain = 3;\n  repeated string searchDomains = 4;\n  repeated string options = 5;\n}\n\nmessage ConfigureDnsResponse {}\n\nmessage ConfigureHostsRequest {\n  message HostsEntry {\n    string ipAddress = 1;\n    repeated string hostnames = 2;\n    optional string comment = 3;\n  }\n  string location = 1;\n  repeated HostsEntry entries = 2;\n  optional string comment = 3;\n}\n\nmessage ConfigureHostsResponse {}\n\nmessage SyncRequest {}\nmessage SyncResponse {}\n\nmessage KillRequest {\n  int32 pid = 1;\n  int32 signal = 3;\n}\n\nmessage KillResponse { int32 result = 1; }\n\n// Categories of statistics that can be requested.\nenum StatCategory {\n  STAT_CATEGORY_UNSPECIFIED = 0;\n  STAT_CATEGORY_PROCESS = 1;\n  STAT_CATEGORY_MEMORY = 2;\n  STAT_CATEGORY_CPU = 3;\n  STAT_CATEGORY_BLOCK_IO = 4;\n  STAT_CATEGORY_NETWORK = 5;\n  STAT_CATEGORY_MEMORY_EVENTS = 6;\n}\n\nmessage ContainerStatisticsRequest {\n  repeated string container_ids = 1;  // Empty = all containers\n  repeated StatCategory categories = 2;  // Empty = all categories\n}\n\nmessage ContainerStatisticsResponse {\n  repeated ContainerStats containers = 1;\n}\n\nmessage ContainerStats {\n  string container_id = 1;\n  ProcessStats process = 2;\n  MemoryStats memory = 3;\n  CPUStats cpu = 4;\n  BlockIOStats block_io = 5;\n  repeated NetworkStats networks = 6;\n  MemoryEventStats memory_events = 7;\n}\n\nmessage ProcessStats {\n  uint64 current = 1;\n  uint64 limit = 2;  // 0 or max value = unlimited\n}\n\nmessage MemoryStats {\n  uint64 usage_bytes = 1;\n  uint64 limit_bytes = 2;\n  uint64 swap_usage_bytes = 3;\n  uint64 swap_limit_bytes = 4;\n  uint64 cache_bytes = 5;\n  uint64 kernel_stack_bytes = 6;\n  uint64 slab_bytes = 7;\n  uint64 page_faults = 8;\n  uint64 major_page_faults = 9;\n  uint64 inactive_file = 10;\n  uint64 anon = 11;\n}\n\nmessage CPUStats {\n  uint64 usage_usec = 1;\n  uint64 user_usec = 2;\n  uint64 system_usec = 3;\n  uint64 throttling_periods = 4;\n  uint64 throttled_periods = 5;\n  uint64 throttled_time_usec = 6;\n}\n\nmessage BlockIOStats {\n  repeated BlockIOEntry devices = 1;\n}\n\nmessage BlockIOEntry {\n  uint64 major = 1;\n  uint64 minor = 2;\n  uint64 read_bytes = 3;\n  uint64 write_bytes = 4;\n  uint64 read_operations = 5;\n  uint64 write_operations = 6;\n}\n\nmessage NetworkStats {\n  string interface = 1;\n  uint64 receivedPackets = 2;\n  uint64 transmittedPackets = 3;\n  uint64 receivedBytes = 4;\n  uint64 transmittedBytes = 5;\n  uint64 receivedErrors = 6;\n  uint64 transmittedErrors = 7;\n}\n\n// Memory event counters from cgroup2's memory.events file.\nmessage MemoryEventStats {\n  // Number of times the cgroup was reclaimed due to low memory.\n  uint64 low = 1;\n  // Number of times the cgroup exceeded its high memory limit.\n  uint64 high = 2;\n  // Number of times the cgroup hit its max memory limit.\n  uint64 max = 3;\n  // Number of times the cgroup triggered OOM.\n  uint64 oom = 4;\n  // Number of processes killed by OOM killer.\n  uint64 oom_kill = 5;\n  // Number of times charge for memory failed because of limit.\n  uint64 oom_group_kill = 6;\n}\n"
  },
  {
    "path": "Sources/Containerization/SystemPlatform.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOCI\n\n/// `SystemPlatform` describes an operating system and architecture pair.\n/// This is primarily used to choose what kind of OCI image to pull from a\n/// registry.\npublic struct SystemPlatform: Sendable, Codable {\n    public enum OS: String, CaseIterable, Sendable, Codable {\n        case linux\n        case darwin\n    }\n    public let os: OS\n\n    public enum Architecture: String, CaseIterable, Sendable, Codable {\n        case arm64\n        case amd64\n    }\n    public let architecture: Architecture\n\n    public func ociPlatform() -> ContainerizationOCI.Platform {\n        ContainerizationOCI.Platform(arch: architecture.rawValue, os: os.rawValue)\n    }\n\n    public static var linuxArm: SystemPlatform { .init(os: .linux, architecture: .arm64) }\n    public static var linuxAmd: SystemPlatform { .init(os: .linux, architecture: .amd64) }\n}\n"
  },
  {
    "path": "Sources/Containerization/TimeSyncer.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Logging\n\nactor TimeSyncer {\n    private var task: Task<Void, Never>?\n    private var context: Vminitd?\n    private var paused: Bool\n    private let logger: Logger?\n\n    init(logger: Logger?) {\n        self.paused = false\n        self.logger = logger\n    }\n\n    func start(context: Vminitd, interval: Duration = .seconds(30)) {\n        guard self.task == nil else {\n            return\n        }\n\n        self.context = context\n        self.task = Task {\n            while true {\n                do {\n                    do {\n                        try await Task.sleep(for: interval)\n                    } catch {\n                        return\n                    }\n\n                    guard !paused else {\n                        continue\n                    }\n\n                    var timeval = timeval()\n                    guard gettimeofday(&timeval, nil) == 0 else {\n                        throw POSIXError.fromErrno()\n                    }\n\n                    try await context.setTime(\n                        sec: Int64(timeval.tv_sec),\n                        usec: Int32(timeval.tv_usec)\n                    )\n                } catch {\n                    self.logger?.error(\"failed to sync time with guest agent: \\(error)\")\n                }\n            }\n        }\n    }\n\n    func pause() async {\n        self.paused = true\n    }\n\n    func resume() async {\n        self.paused = false\n    }\n\n    func close() async throws {\n        guard let task else {\n            // Already closed, nop.\n            return\n        }\n\n        task.cancel()\n        await task.value\n\n        try await self.context?.close()\n        self.task = nil\n        self.context = nil\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/UnixSocketConfiguration.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport SystemPackage\n\n/// Represents a UnixSocket that can be shared into or out of a container/guest.\npublic struct UnixSocketConfiguration: Sendable {\n    // TODO: Realistically, we can just hash this struct and use it as the \"id\".\n    /// The unique identifier for this socket configuration.\n    public var id: String {\n        _id\n    }\n\n    private let _id = UUID().uuidString\n\n    /// The path to the socket you'd like relayed. For .into\n    /// direction this should be the path on the host to a unix socket.\n    /// For direction .outOf this should be the path in the container/guest\n    /// to a unix socket.\n    public var source: URL\n\n    /// The path you'd like the socket to be relayed to. For .into\n    /// direction this should be the path in the container/guest. For\n    /// direction .outOf this should be the path on your host.\n    public var destination: URL\n\n    /// What to set the file permissions of the unix socket being created\n    /// to. For .into direction this will be the socket in the guest. For\n    /// .outOf direction this will be the socket on the host.\n    public var permissions: FilePermissions?\n\n    /// The direction of the relay. `.into` for sharing a unix socket on your\n    /// host into the container/guest. `outOf` shares a socket in the container/guest\n    /// onto your host.\n    public var direction: Direction\n\n    /// Type that denotes the direction of the unix socket relay.\n    public enum Direction: Sendable {\n        /// Share the socket into the container/guest.\n        case into\n        /// Share a socket in the container/guest onto the host.\n        case outOf\n    }\n\n    public init(\n        source: URL,\n        destination: URL,\n        permissions: FilePermissions? = nil,\n        direction: Direction = .into\n    ) {\n        self.source = source\n        self.destination = destination\n        self.permissions = permissions\n        self.direction = direction\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/UnixSocketRelay.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport ContainerizationIO\nimport ContainerizationOS\nimport Foundation\nimport Logging\nimport Synchronization\n\npackage final class UnixSocketRelay: Sendable {\n    private let port: UInt32\n    private let configuration: UnixSocketConfiguration\n    private let vm: any VirtualMachineInstance\n    private let queue: DispatchQueue\n    private let log: Logger?\n    private let state: Mutex<State>\n\n    private struct State {\n        var activeRelays: [String: BidirectionalRelay] = [:]\n        var t: Task<(), Never>? = nil\n        var listener: VsockListener? = nil\n    }\n\n    init(\n        port: UInt32,\n        socket: UnixSocketConfiguration,\n        vm: any VirtualMachineInstance,\n        queue: DispatchQueue,\n        log: Logger? = nil\n    ) throws {\n        self.port = port\n        self.configuration = socket\n        self.vm = vm\n        self.queue = queue\n        self.log = log\n        self.state = Mutex<State>(.init())\n    }\n\n    deinit {\n        state.withLock { $0.t?.cancel() }\n    }\n}\n\nextension UnixSocketRelay {\n    func start() async throws {\n        switch configuration.direction {\n        case .outOf:\n            try await setupHostVsockDial()\n        case .into:\n            try setupHostVsockListener()\n        }\n    }\n\n    func stop() throws {\n        try state.withLock {\n            guard let t = $0.t else {\n                throw ContainerizationError(\n                    .invalidState,\n                    message: \"failed to stop socket relay: relay has not been started\"\n                )\n            }\n            t.cancel()\n            $0.t = nil\n            for (_, relay) in $0.activeRelays {\n                relay.stop()\n            }\n            $0.activeRelays.removeAll()\n\n            switch configuration.direction {\n            case .outOf:\n                // If we created the host conn, lets unlink it also. It's possible it was\n                // already unlinked if the relay failed earlier.\n                try? FileManager.default.removeItem(at: self.configuration.destination)\n            case .into:\n                try $0.listener?.finish()\n            }\n        }\n    }\n\n    private func setupHostVsockDial() async throws {\n        let hostConn = configuration.destination\n\n        let socketType = try UnixType(\n            path: hostConn.path,\n            unlinkExisting: true\n        )\n        let hostSocket = try Socket(type: socketType)\n        try hostSocket.listen()\n\n        log?.info(\n            \"listening on host UDS\",\n            metadata: [\n                \"path\": \"\\(hostConn.path)\",\n                \"vport\": \"\\(port)\",\n            ])\n        let connectionStream = try hostSocket.acceptStream(closeOnDeinit: false)\n        state.withLock {\n            $0.t = Task {\n                do {\n                    for try await connection in connectionStream {\n                        try await self.handleHostUnixConn(\n                            hostConn: connection,\n                            port: self.port,\n                            vm: self.vm,\n                            log: self.log\n                        )\n                    }\n                } catch {\n                    log?.error(\"failed in unix socket relay loop: \\(error)\")\n                }\n                try? FileManager.default.removeItem(at: hostConn)\n            }\n        }\n    }\n\n    private func setupHostVsockListener() throws {\n        let hostPath = configuration.source\n\n        let listener = try vm.listen(port)\n        log?.info(\n            \"listening on guest vsock\",\n            metadata: [\n                \"path\": \"\\(hostPath)\",\n                \"vport\": \"\\(port)\",\n            ])\n\n        state.withLock {\n            $0.listener = listener\n            $0.t = Task {\n                do {\n                    defer { try? listener.finish() }\n                    for await connection in listener {\n                        try await self.handleGuestVsockConn(\n                            vsockConn: connection,\n                            hostConnectionPath: hostPath,\n                            port: self.port,\n                            log: self.log\n                        )\n                    }\n                } catch {\n                    self.log?.error(\"failed to setup relay between vsock \\(self.port) and \\(hostPath.path): \\(error)\")\n                }\n            }\n        }\n    }\n\n    private func handleHostUnixConn(\n        hostConn: ContainerizationOS.Socket,\n        port: UInt32,\n        vm: any VirtualMachineInstance,\n        log: Logger?\n    ) async throws {\n        do {\n            let guestConn = try await vm.dial(port)\n            log?.debug(\n                \"initiating connection from host to guest\",\n                metadata: [\n                    \"vport\": \"\\(port)\",\n                    \"hostFd\": \"\\(guestConn.fileDescriptor)\",\n                    \"guestFd\": \"\\(hostConn.fileDescriptor)\",\n                ])\n            try await self.relay(\n                hostConn: hostConn,\n                guestFd: guestConn.fileDescriptor\n            )\n        } catch {\n            log?.error(\"failed to relay between vsock \\(port) and \\(hostConn)\")\n            throw error\n        }\n    }\n\n    private func handleGuestVsockConn(\n        vsockConn: FileHandle,\n        hostConnectionPath: URL,\n        port: UInt32,\n        log: Logger?\n    ) async throws {\n        let hostPath = hostConnectionPath.path\n        let socketType = try UnixType(path: hostPath)\n        let hostSocket = try Socket(\n            type: socketType,\n            closeOnDeinit: false\n        )\n        log?.debug(\n            \"initiating connection from guest to host\",\n            metadata: [\n                \"vport\": \"\\(port)\",\n                \"hostFd\": \"\\(hostSocket.fileDescriptor)\",\n                \"guestFd\": \"\\(vsockConn.fileDescriptor)\",\n            ])\n        try hostSocket.connect()\n\n        do {\n            try await self.relay(\n                hostConn: hostSocket,\n                guestFd: vsockConn.fileDescriptor\n            )\n        } catch {\n            log?.error(\"failed to relay between vsock \\(port) and \\(hostPath)\")\n        }\n    }\n\n    private func relay(\n        hostConn: Socket,\n        guestFd: Int32\n    ) async throws {\n        let hostFd = hostConn.fileDescriptor\n\n        let relayID = UUID().uuidString\n        let relay = BidirectionalRelay(\n            fd1: hostFd,\n            fd2: guestFd,\n            queue: queue,\n            log: log\n        )\n\n        state.withLock {\n            $0.activeRelays[relayID] = relay\n        }\n\n        relay.start()\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/UnixSocketRelayManager.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport Foundation\nimport Logging\n\npackage actor UnixSocketRelayManager {\n    private let vm: any VirtualMachineInstance\n    private var relays: [String: UnixSocketRelay]\n    private let queue: DispatchQueue\n    private let log: Logger?\n\n    init(vm: any VirtualMachineInstance, log: Logger? = nil) {\n        self.vm = vm\n        self.relays = [:]\n        self.queue = DispatchQueue(label: \"com.apple.containerization.socket-relay\")\n        self.log = log\n    }\n}\n\nextension UnixSocketRelayManager {\n    func start(port: UInt32, socket: UnixSocketConfiguration) async throws {\n        guard relays[socket.id] == nil else {\n            throw ContainerizationError(\n                .invalidState,\n                message: \"socket relay \\(socket.id) already started\"\n            )\n        }\n\n        let relay = try UnixSocketRelay(\n            port: port,\n            socket: socket,\n            vm: vm,\n            queue: queue,\n            log: log\n        )\n\n        do {\n            relays[socket.id] = relay\n            try await relay.start()\n        } catch {\n            relays.removeValue(forKey: socket.id)\n            throw error\n        }\n    }\n\n    func stop(socket: UnixSocketConfiguration) async throws {\n        guard let storedRelay = relays.removeValue(forKey: socket.id) else {\n            throw ContainerizationError(\n                .notFound,\n                message: \"failed to stop socket relay\"\n            )\n        }\n        try storedRelay.stop()\n    }\n\n    func stopAll() async throws {\n        for (_, relay) in relays {\n            try relay.stop()\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/VMConfiguration.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOCI\nimport Foundation\n\n/// Destination for boot log (serial console) output.\npublic struct BootLog: Sendable {\n    /// The underlying representation of the boot log destination.\n    internal enum Representation: Sendable {\n        case file(path: URL, append: Bool)\n        case fileHandle(FileHandle)\n    }\n\n    internal var base: Representation\n\n    /// Write boot logs to a file at the specified path.\n    ///\n    /// - Parameters:\n    ///   - path: The URL of the file to write boot logs to.\n    ///   - append: Whether to append to an existing file or overwrite it. Defaults to true.\n    ///\n    /// - Returns: A boot log destination that writes to a file.\n    public static func file(path: URL, append: Bool = true) -> BootLog {\n        self.init(base: .file(path: path, append: append))\n    }\n\n    /// Write boot logs to a file handle.\n    ///\n    /// - Parameter fileHandle: The file handle to write boot logs to.\n    ///\n    /// - Returns: A boot log destination that writes to a file handle.\n    public static func fileHandle(_ fileHandle: FileHandle) -> BootLog {\n        self.init(base: .fileHandle(fileHandle))\n    }\n}\n\n/// Protocol for VM creation configuration. Allows VMMs to extend with specific settings\n/// while maintaining a common core configuration.\npublic protocol VMCreationConfig: Sendable {\n    /// The common VM configuration that all VMMs must support.\n    var configuration: VMConfiguration { get }\n}\n\n/// Standard VM creation configuration with only common settings.\npublic struct StandardVMConfig: VMCreationConfig {\n    public var configuration: VMConfiguration\n\n    public init(configuration: VMConfiguration) {\n        self.configuration = configuration\n    }\n}\n\n/// Configuration for creating a virtual machine instance.\npublic struct VMConfiguration: Sendable {\n    /// The amount of CPUs to allocate.\n    public var cpus: Int\n    /// The memory in bytes to allocate.\n    public var memoryInBytes: UInt64\n    /// The network interfaces to attach.\n    public var interfaces: [any Interface]\n    /// Mounts organized by metadata ID (e.g. container ID).\n    /// Each ID maps to an array of mounts for that workload.\n    public var mountsByID: [String: [Mount]]\n    /// Optional destination for serial boot logs.\n    public var bootLog: BootLog?\n    /// Enable nested virtualization support. If the VirtualMachineManager\n    /// does not support this feature, it MUST return an .unsupported ContainerizationError.\n    public var nestedVirtualization: Bool\n\n    public init(\n        cpus: Int = 4,\n        memoryInBytes: UInt64 = 1024 * 1024 * 1024,\n        interfaces: [any Interface] = [],\n        mountsByID: [String: [Mount]] = [:],\n        bootLog: BootLog? = nil,\n        nestedVirtualization: Bool = false\n    ) {\n        self.cpus = cpus\n        self.memoryInBytes = memoryInBytes\n        self.interfaces = interfaces\n        self.mountsByID = mountsByID\n        self.bootLog = bootLog\n        self.nestedVirtualization = nestedVirtualization\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/VZVirtualMachine+Helpers.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(macOS)\nimport Foundation\nimport Logging\nimport Virtualization\nimport ContainerizationError\n\nextension VZVirtualMachine {\n    nonisolated func connect(queue: DispatchQueue, port: UInt32) async throws -> VZVirtioSocketConnection {\n        try await withCheckedThrowingContinuation { cont in\n            queue.sync {\n                guard let vsock = self.socketDevices[0] as? VZVirtioSocketDevice else {\n                    let error = ContainerizationError(.invalidArgument, message: \"no vsock device\")\n                    cont.resume(throwing: error)\n                    return\n                }\n                vsock.connect(toPort: port) { result in\n                    switch result {\n                    case .success(let conn):\n                        // `conn` isn't used concurrently.\n                        nonisolated(unsafe) let conn = conn\n                        cont.resume(returning: conn)\n                    case .failure(let error):\n                        cont.resume(throwing: error)\n                    }\n                }\n            }\n        }\n    }\n\n    func listen(queue: DispatchQueue, port: UInt32, listener: VZVirtioSocketListener) throws {\n        try queue.sync {\n            guard let vsock = self.socketDevices[0] as? VZVirtioSocketDevice else {\n                throw ContainerizationError(.invalidArgument, message: \"no vsock device\")\n            }\n            vsock.setSocketListener(listener, forPort: port)\n        }\n    }\n\n    func removeListener(queue: DispatchQueue, port: UInt32) throws {\n        try queue.sync {\n            guard let vsock = self.socketDevices[0] as? VZVirtioSocketDevice else {\n                throw ContainerizationError(\n                    .invalidArgument,\n                    message: \"no vsock device to remove\"\n                )\n            }\n            vsock.removeSocketListener(forPort: port)\n        }\n    }\n\n    func start(queue: DispatchQueue) async throws {\n        try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in\n            queue.sync {\n                self.start { result in\n                    if case .failure(let error) = result {\n                        cont.resume(throwing: error)\n                        return\n                    }\n                    cont.resume()\n                }\n            }\n        }\n    }\n\n    func stop(queue: DispatchQueue) async throws {\n        try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in\n            queue.sync {\n                self.stop { error in\n                    if let error {\n                        cont.resume(throwing: error)\n                        return\n                    }\n                    cont.resume()\n                }\n            }\n        }\n    }\n\n    func pause(queue: DispatchQueue) async throws {\n        try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in\n            queue.sync {\n                self.pause { result in\n                    if case .failure(let error) = result {\n                        cont.resume(throwing: error)\n                        return\n                    }\n                    cont.resume()\n                }\n            }\n        }\n    }\n\n    func resume(queue: DispatchQueue) async throws {\n        try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in\n            queue.sync {\n                self.resume { result in\n                    if case .failure(let error) = result {\n                        cont.resume(throwing: error)\n                        return\n                    }\n                    cont.resume()\n                }\n            }\n        }\n    }\n}\n\nextension VZVirtualMachine {\n    func waitForAgent(queue: DispatchQueue) async throws -> FileHandle {\n        let agentConnectionRetryCount: Int = 200\n        let agentConnectionSleepDuration: Duration = .milliseconds(20)\n\n        for _ in 0...agentConnectionRetryCount {\n            do {\n                return try await self.connect(queue: queue, port: Vminitd.port).dupHandle()\n            } catch {\n                try await Task.sleep(for: agentConnectionSleepDuration)\n                continue\n            }\n        }\n        throw ContainerizationError(.timeout, message: \"failed to get a connection to agent socket\")\n    }\n}\n\nextension VZVirtioSocketConnection {\n    func dupHandle() throws -> FileHandle {\n        let fd = dup(self.fileDescriptor)\n        if fd == -1 {\n            throw POSIXError.fromErrno()\n        }\n        self.close()\n        return FileHandle(fileDescriptor: fd, closeOnDealloc: false)\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Containerization/VZVirtualMachineInstance.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(macOS)\nimport Foundation\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport Logging\nimport NIOCore\nimport NIOPosix\nimport Synchronization\nimport Virtualization\n\nstruct VZVirtualMachineInstance: Sendable {\n    typealias Agent = Vminitd\n\n    /// Attached mounts on the virtual machine, organized by metadata ID.\n    public let mounts: [String: [AttachedFilesystem]]\n\n    /// Returns the runtime state of the vm.\n    public var state: VirtualMachineInstanceState {\n        vzStateToInstanceState()\n    }\n\n    /// The virtual machine instance configuration.\n    private let config: Configuration\n    public struct Configuration: Sendable {\n        /// Amount of cpus to allocated.\n        public var cpus: Int\n        /// Amount of memory in bytes allocated.\n        public var memoryInBytes: UInt64\n        /// Toggle rosetta's x86_64 emulation support.\n        public var rosetta: Bool\n        /// Toggle nested virtualization support.\n        public var nestedVirtualization: Bool\n        /// Mount attachments organized by metadata ID.\n        public var mountsByID: [String: [Mount]]\n        /// Network interface attachments.\n        public var interfaces: [any Interface]\n        /// Kernel image.\n        public var kernel: Kernel?\n        /// The root filesystem.\n        public var initialFilesystem: Mount?\n        /// Destination for the virtual machine's boot logs.\n        public var bootLog: BootLog?\n\n        init() {\n            self.cpus = 4\n            self.memoryInBytes = 1024.mib()\n            self.rosetta = false\n            self.nestedVirtualization = false\n            self.mountsByID = [:]\n            self.interfaces = []\n        }\n    }\n\n    // `vm` isn't used concurrently.\n    private nonisolated(unsafe) let vm: VZVirtualMachine\n    private let queue: DispatchQueue\n    private let lock: AsyncLock\n    private let group: EventLoopGroup\n    private let ownsGroup: Bool\n    private let timeSyncer: TimeSyncer\n    private let logger: Logger?\n\n    public init(\n        group: EventLoopGroup? = nil,\n        logger: Logger? = nil,\n        with: (inout Configuration) throws -> Void\n    ) throws {\n        var config = Configuration()\n        try with(&config)\n        try self.init(group: group, config: config, logger: logger)\n    }\n\n    init(group: EventLoopGroup?, config: Configuration, logger: Logger?) throws {\n        if let group {\n            self.ownsGroup = false\n            self.group = group\n        } else {\n            self.ownsGroup = true\n            self.group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)\n        }\n\n        self.config = config\n        self.lock = .init()\n        self.queue = DispatchQueue(label: \"com.apple.containerization.vzvm.\\(UUID().uuidString)\")\n        self.mounts = try config.mountAttachments()\n        self.logger = logger\n        self.timeSyncer = .init(logger: logger)\n\n        self.vm = VZVirtualMachine(\n            configuration: try config.toVZ(),\n            queue: self.queue\n        )\n    }\n}\n\nextension VZVirtualMachineInstance: VirtualMachineInstance {\n    func start() async throws {\n        try await lock.withLock { _ in\n            guard self.state == .stopped else {\n                throw ContainerizationError(\n                    .invalidState,\n                    message: \"virtual machine is not stopped \\(self.state)\"\n                )\n            }\n\n            // Do any necessary setup needed prior to starting the guest.\n            try await self.prestart()\n\n            try await self.vm.start(queue: self.queue)\n\n            let agent = Vminitd(\n                connection: try await self.vm.waitForAgent(queue: self.queue),\n                group: self.group\n            )\n\n            do {\n                if self.config.rosetta {\n                    try await agent.enableRosetta()\n                }\n            } catch {\n                try await agent.close()\n                throw error\n            }\n\n            // Don't close our remote context as we are providing\n            // it to our time sync routine.\n            await self.timeSyncer.start(context: agent)\n        }\n    }\n\n    func stop() async throws {\n        try await lock.withLock { connections in\n            // NOTE: We should record HOW the vm stopped eventually. If the vm exited\n            // unexpectedly virtualization framework offers you a way to store\n            // an error on how it exited. We should report that here instead of the\n            // generic vm is not running.\n            guard self.state == .running else {\n                throw ContainerizationError(.invalidState, message: \"vm is not running\")\n            }\n\n            try await self.timeSyncer.close()\n\n            if self.ownsGroup {\n                try await self.group.shutdownGracefully()\n            }\n\n            try await self.vm.stop(queue: self.queue)\n        }\n    }\n\n    // NOTE: Investigate what is the \"right\" way to handle already vended vsock\n    // connections for pause and resume.\n\n    func pause() async throws {\n        try await lock.withLock { _ in\n            await self.timeSyncer.pause()\n            try await self.vm.pause(queue: self.queue)\n        }\n    }\n\n    func resume() async throws {\n        try await lock.withLock { _ in\n            try await self.vm.resume(queue: self.queue)\n            await self.timeSyncer.resume()\n        }\n    }\n\n    public func dialAgent() async throws -> Vminitd {\n        try await lock.withLock { _ in\n            do {\n                let conn = try await vm.connect(\n                    queue: queue,\n                    port: Vminitd.port\n                )\n                let handle = try conn.dupHandle()\n                let agent = Vminitd(connection: handle, group: self.group)\n                return agent\n            } catch {\n                if let err = error as? ContainerizationError {\n                    throw err\n                }\n                throw ContainerizationError(\n                    .internalError,\n                    message: \"failed to dial agent\",\n                    cause: error\n                )\n            }\n        }\n    }\n\n    func dial(_ port: UInt32) async throws -> FileHandle {\n        try await lock.withLock { _ in\n            do {\n                let conn = try await vm.connect(\n                    queue: queue,\n                    port: port\n                )\n                return try conn.dupHandle()\n            } catch {\n                if let err = error as? ContainerizationError {\n                    throw err\n                }\n                throw ContainerizationError(\n                    .internalError,\n                    message: \"failed to dial vsock port\",\n                    cause: error\n                )\n            }\n        }\n    }\n\n    func listen(_ port: UInt32) throws -> VsockListener {\n        let stream = VsockListener(port: port, stopListen: self.stopListen)\n        let listener = VZVirtioSocketListener()\n        listener.delegate = stream\n\n        try self.vm.listen(\n            queue: queue,\n            port: port,\n            listener: listener\n        )\n        return stream\n    }\n\n    private func stopListen(_ port: UInt32) throws {\n        try self.vm.removeListener(\n            queue: queue,\n            port: port\n        )\n    }\n}\n\nextension VZVirtualMachineInstance {\n    func vzStateToInstanceState() -> VirtualMachineInstanceState {\n        self.queue.sync {\n            let state: VirtualMachineInstanceState\n            switch self.vm.state {\n            case .starting:\n                state = .starting\n            case .running:\n                state = .running\n            case .stopping:\n                state = .stopping\n            case .stopped:\n                state = .stopped\n            default:\n                state = .unknown\n            }\n            return state\n        }\n    }\n\n    func prestart() async throws {\n        if self.config.rosetta {\n            #if arch(arm64)\n            if VZLinuxRosettaDirectoryShare.availability == .notInstalled {\n                self.logger?.info(\"installing rosetta\")\n                try await VZVirtualMachineInstance.Configuration.installRosetta()\n            }\n            #else\n            fatalError(\"rosetta is only supported on arm64\")\n            #endif\n        }\n    }\n}\n\nextension VZVirtualMachineInstance.Configuration {\n    public static func installRosetta() async throws {\n        do {\n            #if arch(arm64)\n            try await VZLinuxRosettaDirectoryShare.installRosetta()\n            #else\n            fatalError(\"rosetta is only supported on arm64\")\n            #endif\n        } catch {\n            throw ContainerizationError(\n                .internalError,\n                message: \"failed to install rosetta\",\n                cause: error\n            )\n        }\n    }\n\n    private func serialPort(destination: BootLog) throws -> [VZVirtioConsoleDeviceSerialPortConfiguration] {\n        let c = VZVirtioConsoleDeviceSerialPortConfiguration()\n        switch destination.base {\n        case .file(let path, let append):\n            c.attachment = try VZFileSerialPortAttachment(url: path, append: append)\n        case .fileHandle(let fileHandle):\n            c.attachment = VZFileHandleSerialPortAttachment(\n                fileHandleForReading: nil,\n                fileHandleForWriting: fileHandle\n            )\n        }\n        return [c]\n    }\n\n    func toVZ() throws -> VZVirtualMachineConfiguration {\n        var config = VZVirtualMachineConfiguration()\n\n        config.cpuCount = self.cpus\n        config.memorySize = self.memoryInBytes\n        config.entropyDevices = [VZVirtioEntropyDeviceConfiguration()]\n        config.socketDevices = [VZVirtioSocketDeviceConfiguration()]\n\n        if let bootLog = self.bootLog {\n            config.serialPorts = try serialPort(destination: bootLog)\n        } else {\n            // We always supply a serial console. If no explicit path was provided just send em to the void.\n            config.serialPorts = try serialPort(destination: .file(path: URL(filePath: \"/dev/null\")))\n        }\n\n        config.networkDevices = try self.interfaces.map {\n            guard let vzi = $0 as? VZInterface else {\n                throw ContainerizationError(.invalidArgument, message: \"interface type not supported by VZ\")\n            }\n            return try vzi.device()\n        }\n\n        if self.rosetta {\n            #if arch(arm64)\n            switch VZLinuxRosettaDirectoryShare.availability {\n            case .notSupported:\n                throw ContainerizationError(\n                    .invalidArgument,\n                    message: \"rosetta was requested but is not supported on this machine\"\n                )\n            case .notInstalled:\n                // NOTE: If rosetta isn't installed, we'll error with a nice error message\n                // during .start() of the virtual machine instance.\n                fallthrough\n            case .installed:\n                let share = try VZLinuxRosettaDirectoryShare()\n                let device = VZVirtioFileSystemDeviceConfiguration(tag: \"rosetta\")\n                device.share = share\n                config.directorySharingDevices.append(device)\n            @unknown default:\n                throw ContainerizationError(\n                    .invalidArgument,\n                    message: \"unknown rosetta availability encountered: \\(VZLinuxRosettaDirectoryShare.availability)\"\n                )\n            }\n            #else\n            fatalError(\"rosetta is only supported on arm64\")\n            #endif\n        }\n\n        guard let kernel = self.kernel else {\n            throw ContainerizationError(.invalidArgument, message: \"kernel cannot be nil\")\n        }\n\n        guard let initialFilesystem = self.initialFilesystem else {\n            throw ContainerizationError(.invalidArgument, message: \"rootfs cannot be nil\")\n        }\n\n        let loader = VZLinuxBootLoader(kernelURL: kernel.path)\n        loader.commandLine = kernel.linuxCommandline(initialFilesystem: initialFilesystem)\n        config.bootLoader = loader\n\n        try initialFilesystem.configure(config: &config)\n\n        // Track used virtiofs tags to avoid creating duplicate VZ devices.\n        // The same source directory mounted to multiple destinations shares one device.\n        var usedVirtioFSTags: Set<String> = []\n        for (_, mounts) in self.mountsByID {\n            for mount in mounts {\n                if case .virtiofs = mount.runtimeOptions {\n                    let tag = try hashMountSource(source: mount.source)\n                    if usedVirtioFSTags.contains(tag) {\n                        continue\n                    }\n                    usedVirtioFSTags.insert(tag)\n                }\n                try mount.configure(config: &config)\n            }\n        }\n\n        let platform = VZGenericPlatformConfiguration()\n        // We shouldn't silently succeed if the user asked for virt and their hardware does\n        // not support it.\n        if !VZGenericPlatformConfiguration.isNestedVirtualizationSupported && self.nestedVirtualization {\n            throw ContainerizationError(\n                .unsupported,\n                message: \"nested virtualization is not supported on the platform\"\n            )\n        }\n        platform.isNestedVirtualizationEnabled = self.nestedVirtualization\n        config.platform = platform\n\n        try config.validate()\n        return config\n    }\n\n    func mountAttachments() throws -> [String: [AttachedFilesystem]] {\n        let allocator = Character.blockDeviceTagAllocator()\n        if let initialFilesystem {\n            // When the initial filesystem is a blk, allocate the first letter \"vd(a)\"\n            // as that is what this blk will be attached under.\n            if initialFilesystem.isBlock {\n                _ = try allocator.allocate()\n            }\n        }\n\n        var attachmentsByID: [String: [AttachedFilesystem]] = [:]\n        for (id, mounts) in self.mountsByID {\n            var attachments: [AttachedFilesystem] = []\n            for mount in mounts {\n                attachments.append(try .init(mount: mount, allocator: allocator))\n            }\n            attachmentsByID[id] = attachments\n        }\n        return attachmentsByID\n    }\n}\n\nextension Kernel {\n    func linuxCommandline(initialFilesystem: Mount) -> String {\n        var args = self.commandLine.kernelArgs\n\n        args.append(\"init=/sbin/vminitd\")\n        // rootfs is always set as ro.\n        args.append(\"ro\")\n\n        switch initialFilesystem.type {\n        case \"virtiofs\":\n            args.append(contentsOf: [\n                \"rootfstype=virtiofs\",\n                \"root=rootfs\",\n            ])\n        case \"ext4\":\n            args.append(contentsOf: [\n                \"rootfstype=ext4\",\n                \"root=/dev/vda\",\n            ])\n        default:\n            fatalError(\"unsupported initfs filesystem \\(initialFilesystem.type)\")\n        }\n\n        if self.commandLine.initArgs.count > 0 {\n            args.append(\"--\")\n            args.append(contentsOf: self.commandLine.initArgs)\n        }\n\n        return args.joined(separator: \" \")\n    }\n}\n\npublic protocol VZInterface {\n    func device() throws -> VZVirtioNetworkDeviceConfiguration\n}\n\nextension NATInterface: VZInterface {\n    public func device() throws -> VZVirtioNetworkDeviceConfiguration {\n        let config = VZVirtioNetworkDeviceConfiguration()\n        if let macAddress = self.macAddress {\n            guard let mac = VZMACAddress(string: macAddress.description) else {\n                throw ContainerizationError(.invalidArgument, message: \"invalid mac address \\(macAddress)\")\n            }\n            config.macAddress = mac\n        }\n        config.attachment = VZNATNetworkDeviceAttachment()\n        return config\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Containerization/VZVirtualMachineManager.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(macOS)\nimport ContainerizationError\nimport ContainerizationOCI\nimport Foundation\nimport Logging\nimport NIOCore\n\n/// A virtualization.framework backed `VirtualMachineManager` implementation.\npublic struct VZVirtualMachineManager: VirtualMachineManager {\n    private let kernel: Kernel\n    private let initialFilesystem: Mount\n    private let rosetta: Bool\n    private let nestedVirtualization: Bool\n    private let group: EventLoopGroup?\n    private let logger: Logger?\n\n    public init(\n        kernel: Kernel,\n        initialFilesystem: Mount,\n        rosetta: Bool = false,\n        nestedVirtualization: Bool = false,\n        group: EventLoopGroup? = nil,\n        logger: Logger? = nil\n    ) {\n        self.kernel = kernel\n        self.initialFilesystem = initialFilesystem\n        self.rosetta = rosetta\n        self.nestedVirtualization = nestedVirtualization\n        self.group = group\n        self.logger = logger\n    }\n\n    public func create(config: some VMCreationConfig) throws -> any VirtualMachineInstance {\n        let vmConfig = config.configuration\n\n        // Use nested virtualization if requested in config or set as default in manager\n        let useNestedVirtualization = vmConfig.nestedVirtualization || self.nestedVirtualization\n\n        return try VZVirtualMachineInstance(\n            group: self.group,\n            logger: self.logger,\n            with: { instanceConfig in\n                instanceConfig.cpus = vmConfig.cpus\n                instanceConfig.memoryInBytes = vmConfig.memoryInBytes\n\n                instanceConfig.kernel = self.kernel\n                instanceConfig.initialFilesystem = self.initialFilesystem\n\n                if let bootLog = vmConfig.bootLog {\n                    instanceConfig.bootLog = bootLog\n                }\n\n                instanceConfig.interfaces = vmConfig.interfaces\n                instanceConfig.rosetta = self.rosetta\n                instanceConfig.nestedVirtualization = useNestedVirtualization\n\n                instanceConfig.mountsByID = vmConfig.mountsByID\n            })\n    }\n}\n#endif\n"
  },
  {
    "path": "Sources/Containerization/VirtualMachineAgent+Additions.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// Protocol to conform to if your agent is capable of relaying unix domain socket\n/// connections.\npublic protocol SocketRelayAgent {\n    func relaySocket(port: UInt32, configuration: UnixSocketConfiguration) async throws\n    func stopSocketRelay(configuration: UnixSocketConfiguration) async throws\n}\n"
  },
  {
    "path": "Sources/Containerization/VirtualMachineAgent.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport Foundation\n\npublic struct WriteFileFlags {\n    public var createParentDirectories = false\n    public var append = false\n    public var create = false\n}\n\n/// A protocol for the agent running inside a virtual machine. If an operation isn't\n/// supported the implementation MUST return a ContainerizationError with a code of\n/// `.unsupported`.\npublic protocol VirtualMachineAgent: Sendable {\n    /// Perform a platform specific standard setup\n    /// of the runtime environment.\n    func standardSetup() async throws\n    /// Close any resources held by the agent.\n    func close() async throws\n\n    // POSIX-y\n    func getenv(key: String) async throws -> String\n    func setenv(key: String, value: String) async throws\n    func mount(_ mount: ContainerizationOCI.Mount) async throws\n    func umount(path: String, flags: Int32) async throws\n    func mkdir(path: String, all: Bool, perms: UInt32) async throws\n    @discardableResult\n    func kill(pid: Int32, signal: Int32) async throws -> Int32\n    func sync() async throws\n    func writeFile(path: String, data: Data, flags: WriteFileFlags, mode: UInt32) async throws\n\n    // Process lifecycle\n    func createProcess(\n        id: String,\n        containerID: String?,\n        stdinPort: UInt32?,\n        stdoutPort: UInt32?,\n        stderrPort: UInt32?,\n        ociRuntimePath: String?,\n        configuration: ContainerizationOCI.Spec,\n        options: Data?\n    ) async throws\n    func startProcess(id: String, containerID: String?) async throws -> Int32\n    func signalProcess(id: String, containerID: String?, signal: Int32) async throws\n    func resizeProcess(id: String, containerID: String?, columns: UInt32, rows: UInt32) async throws\n    func waitProcess(id: String, containerID: String?, timeoutInSeconds: Int64?) async throws -> ExitStatus\n    func deleteProcess(id: String, containerID: String?) async throws\n    func closeProcessStdin(id: String, containerID: String?) async throws\n\n    // Networking\n    func up(name: String, mtu: UInt32?) async throws\n    func down(name: String) async throws\n    func addressAdd(name: String, ipv4Address: CIDRv4) async throws\n    func routeAddLink(name: String, dstIPv4Addr: IPv4Address, srcIPv4Addr: IPv4Address?) async throws\n    func routeAddDefault(name: String, ipv4Gateway: IPv4Address) async throws\n    func configureDNS(config: DNS, location: String) async throws\n    func configureHosts(config: Hosts, location: String) async throws\n\n    // Container statistics\n    func containerStatistics(containerIDs: [String], categories: StatCategory) async throws -> [ContainerStatistics]\n}\n\nextension VirtualMachineAgent {\n    public func closeProcessStdin(id: String, containerID: String?) async throws {\n        throw ContainerizationError(.unsupported, message: \"closeProcessStdin\")\n    }\n\n    public func configureHosts(config: Hosts, location: String) async throws {\n        throw ContainerizationError(.unsupported, message: \"configureHosts\")\n    }\n\n    public func writeFile(path: String, data: Data, flags: WriteFileFlags, mode: UInt32) async throws {\n        throw ContainerizationError(.unsupported, message: \"writeFile\")\n    }\n\n    public func containerStatistics(containerIDs: [String], categories: StatCategory) async throws -> [ContainerStatistics] {\n        throw ContainerizationError(.unsupported, message: \"containerStatistics\")\n    }\n\n    public func sync() async throws {\n        throw ContainerizationError(.unsupported, message: \"sync\")\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/VirtualMachineInstance.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport Foundation\n\n/// The runtime state of the virtual machine instance.\npublic enum VirtualMachineInstanceState: Sendable {\n    case starting\n    case running\n    case stopped\n    case stopping\n    case unknown\n}\n\n/// A live instance of a virtual machine.\npublic protocol VirtualMachineInstance: Sendable {\n    associatedtype Agent: VirtualMachineAgent\n\n    // The state of the virtual machine.\n    var state: VirtualMachineInstanceState { get }\n\n    var mounts: [String: [AttachedFilesystem]] { get }\n    /// Dial the Agent. It's up the VirtualMachineInstance to determine\n    /// what port the agent is listening on.\n    func dialAgent() async throws -> Agent\n    /// Dial a vsock port in the guest.\n    func dial(_ port: UInt32) async throws -> FileHandle\n    /// Listen on a host vsock port.\n    func listen(_ port: UInt32) throws -> VsockListener\n    /// Start the virtual machine.\n    func start() async throws\n    /// Stop the virtual machine.\n    func stop() async throws\n    /// Pause the virtual machine.\n    func pause() async throws\n    /// Resume the virtual machine.\n    func resume() async throws\n}\n\nextension VirtualMachineInstance {\n    func pause() async throws {\n        throw ContainerizationError(.unsupported, message: \"pause\")\n    }\n    func resume() async throws {\n        throw ContainerizationError(.unsupported, message: \"resume\")\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/VirtualMachineManager.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// A protocol to implement for virtual machine isolated containers.\npublic protocol VirtualMachineManager: Sendable {\n    func create(config: some VMCreationConfig) async throws -> any VirtualMachineInstance\n}\n"
  },
  {
    "path": "Sources/Containerization/Vminitd+Rosetta.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOS\nimport Foundation\n\nextension Vminitd {\n    /// Enable Rosetta's x86_64 emulation.\n    public func enableRosetta() async throws {\n        let path = \"/run/rosetta\"\n        try await self.mount(\n            .init(\n                type: \"virtiofs\",\n                source: \"rosetta\",\n                destination: path\n            )\n        )\n        try await self.setupEmulator(\n            binaryPath: \"\\(path)/rosetta\",\n            configuration: Binfmt.Entry.amd64()\n        )\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/Vminitd+SocketRelay.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nextension Vminitd: SocketRelayAgent {\n    /// Sets up a relay between a host socket to a newly created guest socket, or vice versa.\n    public func relaySocket(port: UInt32, configuration: UnixSocketConfiguration) async throws {\n        let request = Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest.with {\n            $0.id = configuration.id\n            $0.vsockPort = port\n\n            if let perms = configuration.permissions {\n                $0.guestSocketPermissions = UInt32(perms.rawValue)\n            }\n\n            switch configuration.direction {\n            case .into:\n                $0.guestPath = configuration.destination.path\n                $0.action = .into\n            case .outOf:\n                $0.guestPath = configuration.source.path\n                $0.action = .outOf\n            }\n        }\n        _ = try await client.proxyVsock(request)\n    }\n\n    /// Stops the specified socket relay.\n    public func stopSocketRelay(configuration: UnixSocketConfiguration) async throws {\n        let request = Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest.with {\n            $0.id = configuration.id\n        }\n        _ = try await client.stopVsockProxy(request)\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/Vminitd.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport ContainerizationOS\nimport Foundation\nimport GRPC\nimport NIOCore\nimport NIOPosix\n\n/// A remote connection into the vminitd Linux guest agent via a port (vsock).\n/// Used to modify the runtime environment of the Linux sandbox.\npublic struct Vminitd: Sendable {\n    public typealias Client = Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncClient\n\n    // Default vsock port that the agent and client use.\n    public static let port: UInt32 = 1024\n\n    let client: Client\n\n    public init(client: Client) {\n        self.client = client\n    }\n\n    public init(connection: FileHandle, group: EventLoopGroup) {\n        self.client = .init(connection: connection, group: group)\n    }\n\n    /// Close the connection to the guest agent.\n    public func close() async throws {\n        try await client.close()\n    }\n}\n\nextension Vminitd: VirtualMachineAgent {\n    /// Perform the standard guest setup necessary for vminitd to be able to\n    /// run containers.\n    public func standardSetup() async throws {\n        try await up(name: \"lo\")\n\n        try await setenv(key: \"PATH\", value: LinuxProcessConfiguration.defaultPath)\n\n        // Vminitd mounts /proc, /sys, /sys/fs/cgroup and /run automatically.\n        let mounts: [ContainerizationOCI.Mount] = [\n            .init(type: \"tmpfs\", source: \"tmpfs\", destination: \"/tmp\"),\n            .init(type: \"devpts\", source: \"devpts\", destination: \"/dev/pts\", options: [\"gid=5\", \"mode=620\", \"ptmxmode=666\"]),\n        ]\n        for mount in mounts {\n            try await self.mount(mount)\n        }\n    }\n\n    public func writeFile(path: String, data: Data, flags: WriteFileFlags, mode: UInt32) async throws {\n        _ = try await client.writeFile(\n            .with {\n                $0.path = path\n                $0.mode = mode\n                $0.data = data\n                $0.flags = .with {\n                    $0.append = flags.append\n                    $0.createIfMissing = flags.create\n                    $0.createParentDirs = flags.createParentDirectories\n                }\n            })\n    }\n\n    /// Get statistics for containers. If `containerIDs` is empty returns stats for all containers\n    /// in the guest. If `categories` is empty, all categories are returned.\n    public func containerStatistics(containerIDs: [String], categories: StatCategory) async throws -> [ContainerStatistics] {\n        let response = try await client.containerStatistics(\n            .with {\n                $0.containerIds = containerIDs\n                $0.categories = categories.toProtoCategories()\n            })\n\n        return response.containers.map { protoStats in\n            ContainerStatistics(\n                id: protoStats.containerID,\n                process: categories.contains(.process) && protoStats.hasProcess\n                    ? .init(\n                        current: protoStats.process.current,\n                        limit: protoStats.process.limit\n                    ) : nil,\n                memory: categories.contains(.memory) && protoStats.hasMemory\n                    ? .init(\n                        usageBytes: protoStats.memory.usageBytes,\n                        limitBytes: protoStats.memory.limitBytes,\n                        swapUsageBytes: protoStats.memory.swapUsageBytes,\n                        swapLimitBytes: protoStats.memory.swapLimitBytes,\n                        cacheBytes: protoStats.memory.cacheBytes,\n                        kernelStackBytes: protoStats.memory.kernelStackBytes,\n                        slabBytes: protoStats.memory.slabBytes,\n                        pageFaults: protoStats.memory.pageFaults,\n                        majorPageFaults: protoStats.memory.majorPageFaults,\n                        inactiveFile: protoStats.memory.inactiveFile,\n                        anon: protoStats.memory.anon\n                    ) : nil,\n                cpu: categories.contains(.cpu) && protoStats.hasCpu\n                    ? .init(\n                        usageUsec: protoStats.cpu.usageUsec,\n                        userUsec: protoStats.cpu.userUsec,\n                        systemUsec: protoStats.cpu.systemUsec,\n                        throttlingPeriods: protoStats.cpu.throttlingPeriods,\n                        throttledPeriods: protoStats.cpu.throttledPeriods,\n                        throttledTimeUsec: protoStats.cpu.throttledTimeUsec\n                    ) : nil,\n                blockIO: categories.contains(.blockIO) && protoStats.hasBlockIo\n                    ? .init(\n                        devices: protoStats.blockIo.devices.map { device in\n                            .init(\n                                major: device.major,\n                                minor: device.minor,\n                                readBytes: device.readBytes,\n                                writeBytes: device.writeBytes,\n                                readOperations: device.readOperations,\n                                writeOperations: device.writeOperations\n                            )\n                        }\n                    ) : nil,\n                networks: categories.contains(.network)\n                    ? protoStats.networks.map { network in\n                        ContainerStatistics.NetworkStatistics(\n                            interface: network.interface,\n                            receivedPackets: network.receivedPackets,\n                            transmittedPackets: network.transmittedPackets,\n                            receivedBytes: network.receivedBytes,\n                            transmittedBytes: network.transmittedBytes,\n                            receivedErrors: network.receivedErrors,\n                            transmittedErrors: network.transmittedErrors\n                        )\n                    } : nil,\n                memoryEvents: categories.contains(.memoryEvents) && protoStats.hasMemoryEvents\n                    ? .init(\n                        low: protoStats.memoryEvents.low,\n                        high: protoStats.memoryEvents.high,\n                        max: protoStats.memoryEvents.max,\n                        oom: protoStats.memoryEvents.oom,\n                        oomKill: protoStats.memoryEvents.oomKill\n                    ) : nil\n            )\n        }\n    }\n\n    /// Mount a filesystem in the sandbox's environment.\n    public func mount(_ mount: ContainerizationOCI.Mount) async throws {\n        _ = try await client.mount(\n            .with {\n                $0.type = mount.type\n                $0.source = mount.source\n                $0.destination = mount.destination\n                $0.options = mount.options\n            })\n    }\n\n    /// Unmount a filesystem in the sandbox's environment.\n    public func umount(path: String, flags: Int32) async throws {\n        _ = try await client.umount(\n            .with {\n                $0.path = path\n                $0.flags = flags\n            })\n    }\n\n    /// Create a directory inside the sandbox's environment.\n    public func mkdir(path: String, all: Bool, perms: UInt32) async throws {\n        _ = try await client.mkdir(\n            .with {\n                $0.path = path\n                $0.all = all\n                $0.perms = perms\n            })\n    }\n\n    public func createProcess(\n        id: String,\n        containerID: String?,\n        stdinPort: UInt32?,\n        stdoutPort: UInt32?,\n        stderrPort: UInt32?,\n        ociRuntimePath: String?,\n        configuration: ContainerizationOCI.Spec,\n        options: Data?\n    ) async throws {\n        let enc = JSONEncoder()\n        _ = try await client.createProcess(\n            .with {\n                $0.id = id\n                if let stdinPort {\n                    $0.stdin = stdinPort\n                }\n                if let stdoutPort {\n                    $0.stdout = stdoutPort\n                }\n                if let stderrPort {\n                    $0.stderr = stderrPort\n                }\n                if let containerID {\n                    $0.containerID = containerID\n                }\n                if let ociRuntimePath {\n                    $0.ociRuntimePath = ociRuntimePath\n                }\n                $0.configuration = try enc.encode(configuration)\n            })\n    }\n\n    @discardableResult\n    public func startProcess(id: String, containerID: String?) async throws -> Int32 {\n        let request = Com_Apple_Containerization_Sandbox_V3_StartProcessRequest.with {\n            $0.id = id\n            if let containerID {\n                $0.containerID = containerID\n            }\n        }\n        let resp = try await client.startProcess(request)\n        return resp.pid\n    }\n\n    public func signalProcess(id: String, containerID: String?, signal: Int32) async throws {\n        let request = Com_Apple_Containerization_Sandbox_V3_KillProcessRequest.with {\n            $0.id = id\n            $0.signal = signal\n            if let containerID {\n                $0.containerID = containerID\n            }\n        }\n        _ = try await client.killProcess(request)\n    }\n\n    public func resizeProcess(id: String, containerID: String?, columns: UInt32, rows: UInt32) async throws {\n        let request = Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest.with {\n            if let containerID {\n                $0.containerID = containerID\n            }\n            $0.id = id\n            $0.columns = columns\n            $0.rows = rows\n        }\n        _ = try await client.resizeProcess(request)\n    }\n\n    public func waitProcess(\n        id: String,\n        containerID: String?,\n        timeoutInSeconds: Int64? = nil\n    ) async throws -> ExitStatus {\n        let request = Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest.with {\n            $0.id = id\n            if let containerID {\n                $0.containerID = containerID\n            }\n        }\n        var callOpts: CallOptions?\n        if let timeoutInSeconds {\n            var copts = CallOptions()\n            copts.timeLimit = .timeout(.seconds(timeoutInSeconds))\n            callOpts = copts\n        }\n        do {\n            let resp = try await client.waitProcess(request, callOptions: callOpts)\n            return ExitStatus(exitCode: resp.exitCode, exitedAt: resp.exitedAt.date)\n        } catch {\n            if let err = error as? GRPCError.RPCTimedOut {\n                throw ContainerizationError(\n                    .timeout,\n                    message: \"failed to wait for process exit within timeout of \\(timeoutInSeconds!) seconds\",\n                    cause: err\n                )\n            }\n            throw error\n        }\n    }\n\n    public func deleteProcess(id: String, containerID: String?) async throws {\n        let request = Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest.with {\n            $0.id = id\n            if let containerID {\n                $0.containerID = containerID\n            }\n        }\n        _ = try await client.deleteProcess(request)\n    }\n\n    public func closeProcessStdin(id: String, containerID: String?) async throws {\n        let request = Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest.with {\n            $0.id = id\n            if let containerID {\n                $0.containerID = containerID\n            }\n        }\n        _ = try await client.closeProcessStdin(request)\n    }\n\n    public func up(name: String, mtu: UInt32? = nil) async throws {\n        let request = Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest.with {\n            $0.interface = name\n            $0.up = true\n            if let mtu { $0.mtu = mtu }\n        }\n        _ = try await client.ipLinkSet(request)\n    }\n\n    public func down(name: String) async throws {\n        let request = Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest.with {\n            $0.interface = name\n            $0.up = false\n        }\n        _ = try await client.ipLinkSet(request)\n    }\n\n    /// Get an environment variable from the sandbox's environment.\n    public func getenv(key: String) async throws -> String {\n        let response = try await client.getenv(\n            .with {\n                $0.key = key\n            })\n        return response.value\n    }\n\n    /// Set an environment variable in the sandbox's environment.\n    public func setenv(key: String, value: String) async throws {\n        _ = try await client.setenv(\n            .with {\n                $0.key = key\n                $0.value = value\n            })\n    }\n}\n\n/// Vminitd specific rpcs.\nextension Vminitd {\n    /// Sets up an emulator in the guest.\n    public func setupEmulator(binaryPath: String, configuration: Binfmt.Entry) async throws {\n        let request = Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest.with {\n            $0.binaryPath = binaryPath\n            $0.name = configuration.name\n            $0.type = configuration.type\n            $0.offset = configuration.offset\n            $0.magic = configuration.magic\n            $0.mask = configuration.mask\n            $0.flags = configuration.flags\n        }\n        _ = try await client.setupEmulator(request)\n    }\n\n    /// Sets the guest time.\n    public func setTime(sec: Int64, usec: Int32) async throws {\n        let request = Com_Apple_Containerization_Sandbox_V3_SetTimeRequest.with {\n            $0.sec = sec\n            $0.usec = usec\n        }\n        _ = try await client.setTime(request)\n    }\n\n    /// Set the provided sysctls inside the Sandbox's environment.\n    public func sysctl(settings: [String: String]) async throws {\n        let request = Com_Apple_Containerization_Sandbox_V3_SysctlRequest.with {\n            $0.settings = settings\n        }\n        _ = try await client.sysctl(request)\n    }\n\n    /// Add an IP address to the sandbox's network interfaces.\n    public func addressAdd(name: String, ipv4Address: CIDRv4) async throws {\n        _ = try await client.ipAddrAdd(\n            .with {\n                $0.interface = name\n                $0.ipv4Address = ipv4Address.description\n            })\n    }\n\n    /// Add a route in the sandbox's environment.\n    public func routeAddLink(name: String, dstIPv4Addr: IPv4Address, srcIPv4Addr: IPv4Address? = nil) async throws {\n        let dstCIDR = \"\\(dstIPv4Addr.description)/32\"\n        _ = try await client.ipRouteAddLink(\n            .with {\n                $0.interface = name\n                $0.dstIpv4Addr = dstCIDR\n                if let srcIPv4Addr {\n                    $0.srcIpv4Addr = srcIPv4Addr.description\n                }\n            })\n    }\n\n    /// Set the default route in the sandbox's environment.\n    public func routeAddDefault(name: String, ipv4Gateway: IPv4Address) async throws {\n        _ = try await client.ipRouteAddDefault(\n            .with {\n                $0.interface = name\n                $0.ipv4Gateway = ipv4Gateway.description\n            })\n    }\n\n    /// Configure DNS within the sandbox's environment.\n    public func configureDNS(config: DNS, location: String) async throws {\n        _ = try await client.configureDns(\n            .with {\n                $0.location = location\n                $0.nameservers = config.nameservers\n                if let domain = config.domain {\n                    $0.domain = domain\n                }\n                $0.searchDomains = config.searchDomains\n                $0.options = config.options\n            })\n    }\n\n    /// Configure /etc/hosts within the sandbox's environment.\n    public func configureHosts(config: Hosts, location: String) async throws {\n        _ = try await client.configureHosts(config.toAgentHostsRequest(location: location))\n    }\n\n    /// Perform a sync call.\n    public func sync() async throws {\n        _ = try await client.sync(.init())\n    }\n\n    public func kill(pid: Int32, signal: Int32) async throws -> Int32 {\n        let response = try await client.kill(\n            .with {\n                $0.pid = pid\n                $0.signal = signal\n            })\n        return response.result\n    }\n\n    /// Metadata received from the guest during a copy operation.\n    public struct CopyMetadata: Sendable {\n        /// Whether the data on the vsock channel is a tar+gzip archive.\n        public let isArchive: Bool\n        /// Total size in bytes (0 if unknown, e.g. for archives).\n        public let totalSize: UInt64\n    }\n\n    /// Unified copy control plane. Sends a CopyRequest over gRPC and processes\n    /// the response stream. Data transfer happens over a separate vsock connection\n    /// managed by the caller.\n    ///\n    /// For COPY_OUT, the `onMetadata` callback is invoked when the guest sends\n    /// metadata (is_archive, total_size) before data transfer begins.\n    /// For COPY_IN, `onMetadata` is not called.\n    public func copy(\n        direction: Com_Apple_Containerization_Sandbox_V3_CopyRequest.Direction,\n        guestPath: URL,\n        vsockPort: UInt32,\n        mode: UInt32 = 0,\n        createParents: Bool = false,\n        isArchive: Bool = false,\n        onMetadata: @Sendable (CopyMetadata) -> Void = { _ in }\n    ) async throws {\n        let request = Com_Apple_Containerization_Sandbox_V3_CopyRequest.with {\n            $0.direction = direction\n            $0.path = guestPath.path\n            $0.mode = mode\n            $0.createParents = createParents\n            $0.vsockPort = vsockPort\n            $0.isArchive = isArchive\n        }\n\n        let stream = client.copy(request)\n\n        for try await response in stream {\n            if !response.error.isEmpty {\n                throw ContainerizationError(.internalError, message: \"copy: \\(response.error)\")\n            }\n            switch response.status {\n            case .metadata:\n                onMetadata(CopyMetadata(isArchive: response.isArchive, totalSize: response.totalSize))\n            case .complete:\n                break\n            case .UNRECOGNIZED(let value):\n                throw ContainerizationError(.internalError, message: \"copy: unrecognized response status \\(value)\")\n            }\n        }\n    }\n}\n\nextension Hosts {\n    func toAgentHostsRequest(location: String) -> Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest {\n        Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest.with {\n            $0.location = location\n            if let comment {\n                $0.comment = comment\n            }\n            $0.entries = entries.map {\n                let entry = $0\n                return Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest.HostsEntry.with {\n                    if let comment = entry.comment {\n                        $0.comment = comment\n                    }\n                    $0.ipAddress = entry.ipAddress\n                    $0.hostnames = entry.hostnames\n                }\n            }\n        }\n    }\n}\n\nextension Vminitd.Client {\n    public init(connection: FileHandle, group: EventLoopGroup) {\n        var config = ClientConnection.Configuration.default(\n            target: .connectedSocket(connection.fileDescriptor),\n            eventLoopGroup: group\n        )\n        config.connectionBackoff = nil\n        config.maximumReceiveMessageLength = Int(64.mib())\n        self = .init(channel: ClientConnection(configuration: config))\n    }\n\n    public func close() async throws {\n        try await self.channel.close().get()\n    }\n}\n\nextension StatCategory {\n    /// Convert StatCategory to proto enum values.\n    func toProtoCategories() -> [Com_Apple_Containerization_Sandbox_V3_StatCategory] {\n        var categories: [Com_Apple_Containerization_Sandbox_V3_StatCategory] = []\n        if contains(.process) {\n            categories.append(.process)\n        }\n        if contains(.memory) {\n            categories.append(.memory)\n        }\n        if contains(.cpu) {\n            categories.append(.cpu)\n        }\n        if contains(.blockIO) {\n            categories.append(.blockIo)\n        }\n        if contains(.network) {\n            categories.append(.network)\n        }\n        if contains(.memoryEvents) {\n            categories.append(.memoryEvents)\n        }\n        return categories\n    }\n}\n"
  },
  {
    "path": "Sources/Containerization/VmnetNetwork.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(macOS)\n\nimport ContainerizationError\nimport ContainerizationExtras\nimport Virtualization\nimport vmnet\n\n/// A network backed by vmnet on macOS.\n@available(macOS 26.0, *)\npublic struct VmnetNetwork: Network {\n    private var allocator: Allocator\n    // `reference` isn't used concurrently.\n    nonisolated(unsafe) private let reference: vmnet_network_ref\n\n    /// The IPv4 subnet of this network.\n    public let subnet: CIDRv4\n\n    /// The IPv4 gateway address of this network.\n    public var ipv4Gateway: IPv4Address {\n        subnet.gateway\n    }\n\n    struct Allocator: Sendable {\n        private let addressAllocator: any AddressAllocator<UInt32>\n        private let cidr: CIDRv4\n        private var allocations: [String: UInt32]\n\n        init(cidr: CIDRv4) throws {\n            self.cidr = cidr\n            self.allocations = .init()\n            let size = Int(cidr.upper.value - cidr.lower.value - 3)\n            self.addressAllocator = try UInt32.rotatingAllocator(\n                lower: cidr.lower.value + 2,\n                size: UInt32(size)\n            )\n        }\n\n        mutating func allocate(_ id: String) throws -> CIDRv4 {\n            if allocations[id] != nil {\n                throw ContainerizationError(.exists, message: \"allocation with id \\(id) already exists\")\n            }\n            let index = try addressAllocator.allocate()\n            allocations[id] = index\n            let ip = IPv4Address(index)\n            return try CIDRv4(ip, prefix: cidr.prefix)\n        }\n\n        mutating func release(_ id: String) throws {\n            if let index = self.allocations[id] {\n                try addressAllocator.release(index)\n                allocations.removeValue(forKey: id)\n            }\n        }\n    }\n\n    /// A network interface supporting the vmnet_network_ref.\n    public struct Interface: Containerization.Interface, VZInterface, Sendable {\n        public let ipv4Address: CIDRv4\n        public let ipv4Gateway: IPv4Address?\n        public let macAddress: MACAddress?\n        public let mtu: UInt32\n\n        // `reference` isn't used concurrently.\n        nonisolated(unsafe) private let reference: vmnet_network_ref\n\n        public init(\n            reference: vmnet_network_ref,\n            ipv4Address: CIDRv4,\n            ipv4Gateway: IPv4Address? = nil,\n            macAddress: MACAddress? = nil,\n            mtu: UInt32 = 1500\n        ) {\n            self.ipv4Address = ipv4Address\n            self.ipv4Gateway = ipv4Gateway\n            self.macAddress = macAddress\n            self.mtu = mtu\n            self.reference = reference\n        }\n\n        /// Returns the underlying `VZVirtioNetworkDeviceConfiguration`.\n        public func device() throws -> VZVirtioNetworkDeviceConfiguration {\n            let config = VZVirtioNetworkDeviceConfiguration()\n            if let macAddress = self.macAddress {\n                guard let mac = VZMACAddress(string: macAddress.description) else {\n                    throw ContainerizationError(.invalidArgument, message: \"invalid mac address \\(macAddress)\")\n                }\n                config.macAddress = mac\n            }\n            config.attachment = VZVmnetNetworkDeviceAttachment(network: self.reference)\n            return config\n        }\n    }\n\n    /// Creates a new network.\n    /// - Parameters:\n    ///   - mode: The vmnet operating mode. Defaults to `.VMNET_SHARED_MODE`.\n    ///   - subnet: The subnet to use for this network.\n    public init(mode: vmnet.operating_modes_t = .VMNET_SHARED_MODE, subnet: CIDRv4? = nil) throws {\n        var status: vmnet_return_t = .VMNET_FAILURE\n        guard let config = vmnet_network_configuration_create(mode, &status) else {\n            throw ContainerizationError(.unsupported, message: \"failed to create vmnet config with status \\(status)\")\n        }\n\n        vmnet_network_configuration_disable_dhcp(config)\n\n        if let subnet {\n            try Self.configureSubnet(config, subnet: subnet)\n        }\n\n        guard let ref = vmnet_network_create(config, &status), status == .VMNET_SUCCESS else {\n            throw ContainerizationError(.unsupported, message: \"failed to create vmnet network with status \\(status)\")\n        }\n\n        let cidr = try Self.getSubnet(ref)\n\n        self.allocator = try .init(cidr: cidr)\n        self.subnet = cidr\n        self.reference = ref\n    }\n\n    /// Returns a new interface for use with a container.\n    /// - Parameter id: The container ID.\n    public mutating func createInterface(_ id: String) throws -> Containerization.Interface? {\n        let ipv4Address = try allocator.allocate(id)\n        return Self.Interface(\n            reference: self.reference,\n            ipv4Address: ipv4Address,\n            ipv4Gateway: self.ipv4Gateway,\n        )\n    }\n\n    /// Returns a new interface without a default gateway route.\n    /// Use this for secondary interfaces where another interface already provides the default route.\n    /// - Parameter id: The container ID.\n    public mutating func createInterfaceWithoutGateway(_ id: String) throws -> Containerization.Interface? {\n        let ipv4Address = try allocator.allocate(id)\n        return Self.Interface(\n            reference: self.reference,\n            ipv4Address: ipv4Address,\n        )\n    }\n\n    /// Returns a new interface for use with a container with a custom MTU.\n    /// - Parameters:\n    ///   - id: The container ID.\n    ///   - mtu: The MTU for the interface.\n    public mutating func createInterface(_ id: String, mtu: UInt32) throws -> Containerization.Interface? {\n        let ipv4Address = try allocator.allocate(id)\n        return Self.Interface(\n            reference: self.reference,\n            ipv4Address: ipv4Address,\n            ipv4Gateway: self.ipv4Gateway,\n            mtu: mtu\n        )\n    }\n\n    /// Performs cleanup of an interface.\n    /// - Parameter id: The container ID.\n    public mutating func releaseInterface(_ id: String) throws {\n        try allocator.release(id)\n    }\n\n    private static func getSubnet(_ ref: vmnet_network_ref) throws -> CIDRv4 {\n        var subnet = in_addr()\n        var mask = in_addr()\n        vmnet_network_get_ipv4_subnet(ref, &subnet, &mask)\n\n        let sa = UInt32(bigEndian: subnet.s_addr)\n        let mv = UInt32(bigEndian: mask.s_addr)\n\n        let lower = IPv4Address(sa & mv)\n        let upper = IPv4Address(lower.value + ~mv)\n\n        return try CIDRv4(lower: lower, upper: upper)\n    }\n\n    private static func configureSubnet(_ config: vmnet_network_configuration_ref, subnet: CIDRv4) throws {\n        let gateway = subnet.gateway\n\n        var ga = in_addr()\n        inet_pton(AF_INET, gateway.description, &ga)\n\n        let mask = IPv4Address(subnet.prefix.prefixMask32)\n        var ma = in_addr()\n        inet_pton(AF_INET, mask.description, &ma)\n\n        guard vmnet_network_configuration_set_ipv4_subnet(config, &ga, &ma) == .VMNET_SUCCESS else {\n            throw ContainerizationError(.internalError, message: \"failed to set subnet \\(subnet) for network\")\n        }\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Containerization/VsockListener.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n#if os(macOS)\nimport Virtualization\n#endif\n\n/// A stream of vsock connections.\npublic final class VsockListener: NSObject, Sendable, AsyncSequence {\n    public typealias Element = FileHandle\n\n    /// The port the connections are for.\n    public let port: UInt32\n\n    private let connections: AsyncStream<FileHandle>\n    private let cont: AsyncStream<FileHandle>.Continuation\n    private let stopListening: @Sendable (_ port: UInt32) throws -> Void\n\n    package init(port: UInt32, stopListen: @Sendable @escaping (_ port: UInt32) throws -> Void) {\n        self.port = port\n        let (stream, continuation) = AsyncStream.makeStream(of: FileHandle.self)\n        self.connections = stream\n        self.cont = continuation\n        self.stopListening = stopListen\n    }\n\n    public func finish() throws {\n        self.cont.finish()\n        try self.stopListening(self.port)\n    }\n\n    public func makeAsyncIterator() -> AsyncStream<FileHandle>.AsyncIterator {\n        connections.makeAsyncIterator()\n    }\n}\n\n#if os(macOS)\n\nextension VsockListener: VZVirtioSocketListenerDelegate {\n    public func listener(\n        _: VZVirtioSocketListener, shouldAcceptNewConnection conn: VZVirtioSocketConnection,\n        from _: VZVirtioSocketDevice\n    ) -> Bool {\n        let fd = dup(conn.fileDescriptor)\n        guard fd != -1 else {\n            return false\n        }\n        conn.close()\n\n        let fh = FileHandle(fileDescriptor: fd, closeOnDealloc: false)\n        let result = cont.yield(fh)\n        if case .terminated = result {\n            try? fh.close()\n            return false\n        }\n\n        return true\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/ContainerizationArchive/ArchiveError.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport CArchive\nimport Foundation\n\n/// An enumeration of the errors that can be thrown while interacting with an archive.\npublic enum ArchiveError: Error, CustomStringConvertible {\n    case unableToCreateArchive\n    case noUnderlyingArchive\n    case noArchiveInCallback\n    case noDelegateConfigured\n    case delegateFreedBeforeCallback\n    case unableToSetFormat(CInt, Format)\n    case unableToAddFilter(CInt, Filter)\n    case unableToWriteEntryHeader(CInt)\n    case unableToWriteData(CLong)\n    case unableToCloseArchive(CInt)\n    case unableToOpenArchive(CInt)\n    case unableToSetOption(CInt)\n    case failedToSetLocale(locales: [String])\n    case failedToGetProperty(String, URLResourceKey)\n    case failedToDetectFilter\n    case failedToDetectFormat\n    case failedToExtractArchive(String)\n    case failedToCreateArchive(String)\n    case invalidBaseAddressArchiveWrite\n\n    /// Description of the error\n    public var description: String {\n        switch self {\n        case .unableToCreateArchive:\n            return \"unable to create an archive.\"\n        case .noUnderlyingArchive:\n            return \"no underlying archive was provided.\"\n        case .noArchiveInCallback:\n            return \"no archive was provided in the callback.\"\n        case .noDelegateConfigured:\n            return \"no delegate was configured.\"\n        case .delegateFreedBeforeCallback:\n            return \"the delegate was freed before the callback was invoked.\"\n        case .unableToSetFormat(let code, let name):\n            return \"unable to set the archive format \\(name), code \\(code)\"\n        case .unableToAddFilter(let code, let name):\n            return \"unable to set the archive filter \\(name), code \\(code)\"\n        case .unableToWriteEntryHeader(let code):\n            return \"unable to write the entry header to the archive, code \\(code)\"\n        case .unableToWriteData(let code):\n            return \"unable to write data to the archive, code \\(code)\"\n        case .unableToCloseArchive(let code):\n            return \"unable to close the archive, code \\(code)\"\n        case .unableToOpenArchive(let code):\n            return \"unable to open the archive, code \\(code)\"\n        case .unableToSetOption(_):\n            return \"unable to set an option on the archive.\"\n        case .failedToSetLocale(let locales):\n            return \"failed to set locale to \\(locales)\"\n        case .failedToGetProperty(let path, let propertyName):\n            return \"failed to read property \\(propertyName) from file at path \\(path)\"\n        case .failedToDetectFilter:\n            return \"failed to detect filter from archive.\"\n        case .failedToDetectFormat:\n            return \"failed to detect format from archive.\"\n        case .failedToExtractArchive(let reason):\n            return \"failed to extract archive: \\(reason)\"\n        case .failedToCreateArchive(let reason):\n            return \"failed to create archive: \\(reason)\"\n        case .invalidBaseAddressArchiveWrite:\n            return \"got an invalid base address for pointer when writing data to archive\"\n        }\n    }\n}\n\npublic struct LibArchiveError: Error {\n    public let source: ArchiveError\n    public let description: String\n}\n\nfunc wrap(_ f: @autoclosure () -> CInt, _ e: (CInt) -> ArchiveError, underlying: OpaquePointer? = nil) throws {\n    let result = f()\n    guard result == ARCHIVE_OK else {\n        let error = e(result)\n        guard let underlying = underlying,\n            let description = archive_error_string(underlying).map(String.init(cString:))\n        else {\n            throw error\n        }\n        throw LibArchiveError(source: error, description: description)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationArchive/ArchiveReader.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport CArchive\nimport ContainerizationError\nimport ContainerizationOS\nimport Foundation\nimport SystemPackage\n\n/// A protocol for reading data in chunks, compatible with both `InputStream` and zero-allocation archive readers.\npublic protocol ReadableStream {\n    /// Reads up to `maxLength` bytes into the provided buffer.\n    /// Returns the number of bytes actually read, 0 for EOF, or -1 for error.\n    func read(_ buffer: UnsafeMutablePointer<UInt8>, maxLength: Int) -> Int\n}\n\nextension InputStream: ReadableStream {}\n\n/// Small wrapper type to read data from an archive entry.\npublic struct ArchiveEntryReader: ReadableStream {\n    private weak var reader: ArchiveReader?\n\n    init(reader: ArchiveReader) {\n        self.reader = reader\n    }\n\n    /// Reads up to `maxLength` bytes into the provided buffer.\n    /// Returns the number of bytes actually read, 0 for EOF, or -1 for error.\n    public func read(_ buffer: UnsafeMutablePointer<UInt8>, maxLength: Int) -> Int {\n        guard let archive = reader?.underlying else { return -1 }\n        let bytesRead = archive_read_data(archive, buffer, maxLength)\n        return bytesRead < 0 ? -1 : bytesRead\n    }\n}\n\n/// A class responsible for reading entries from an archive file.\npublic final class ArchiveReader {\n    private static let chunkSize = 4 * 1024 * 1024\n\n    /// A pointer to the underlying `archive` C structure.\n    var underlying: OpaquePointer?\n    /// The file handle associated with the archive file being read.\n    let fileHandle: FileHandle?\n    /// Temporary decompressed file URL if the input was zstd-compressed\n    private var tempDecompressedFile: URL?\n\n    /// Initializes an `ArchiveReader` to read from a specified file URL with an explicit `Format` and `Filter`.\n    /// Note: This method must be used when it is known that the archive at the specified URL follows the specified\n    /// `Format` and `Filter`.\n    public convenience init(format: Format, filter: Filter, file: URL) throws {\n        // If filter is zstd, decompress it and use filter .none\n        let fileToRead: URL\n        let tempFile: URL?\n        let actualFilter: Filter\n\n        if filter == .zstd {\n            let decompressed = try Self.decompressZstd(file)\n            tempFile = decompressed\n            fileToRead = decompressed\n            actualFilter = .none\n        } else {\n            tempFile = nil\n            fileToRead = file\n            actualFilter = filter\n        }\n\n        let fileHandle = try FileHandle(forReadingFrom: fileToRead)\n        try self.init(format: format, filter: actualFilter, fileHandle: fileHandle)\n        self.tempDecompressedFile = tempFile\n    }\n\n    /// Initializes an `ArchiveReader` to read from the provided file descriptor with an explicit `Format` and `Filter`.\n    /// Note: This method must be used when it is known that the archive pointed to by the file descriptor follows the specified\n    /// `Format` and `Filter`.\n    public init(format: Format, filter: Filter, fileHandle: FileHandle) throws {\n        self.underlying = archive_read_new()\n        self.fileHandle = fileHandle\n\n        try archive_read_set_format(underlying, format.code)\n            .checkOk(elseThrow: .unableToSetFormat(format.code, format))\n        try archive_read_append_filter(underlying, filter.code)\n            .checkOk(elseThrow: .unableToAddFilter(filter.code, filter))\n\n        let fd = fileHandle.fileDescriptor\n        try archive_read_open_fd(underlying, fd, 4096)\n            .checkOk(elseThrow: { .unableToOpenArchive($0) })\n    }\n\n    /// Initialize the `ArchiveReader` to read from a specified file URL\n    /// by trying to auto determine the archives `Format` and `Filter`.\n    public init(file: URL) throws {\n        self.underlying = archive_read_new()\n\n        // Try to decompress as zstd first, fall back to original if it fails\n        let fileToRead: URL\n        if let decompressed = try? Self.decompressZstd(file) {\n            self.tempDecompressedFile = decompressed\n            fileToRead = decompressed\n        } else {\n            fileToRead = file\n        }\n\n        let fileHandle = try FileHandle(forReadingFrom: fileToRead)\n        self.fileHandle = fileHandle\n        try archive_read_support_filter_all(underlying)\n            .checkOk(elseThrow: .failedToDetectFilter)\n        try archive_read_support_format_all(underlying)\n            .checkOk(elseThrow: .failedToDetectFormat)\n        let fd = fileHandle.fileDescriptor\n        try archive_read_open_fd(underlying, fd, 4096)\n            .checkOk(elseThrow: { .unableToOpenArchive($0) })\n    }\n\n    /// Decompress a zstd file to a temporary location\n    private static func decompressZstd(_ source: URL) throws -> URL {\n        guard let tempDir = createTemporaryDirectory(baseName: \"zstd-decompress\") else {\n            throw ArchiveError.failedToDetectFormat\n        }\n        let tempFile = tempDir.appendingPathComponent(\n            source.deletingPathExtension().lastPathComponent\n        )\n\n        let srcPath = source.scheme == nil || source.scheme == \"\" ? source.path : source.path\n        let srcFd = open(srcPath, O_RDONLY)\n        guard srcFd >= 0 else { throw ArchiveError.failedToDetectFormat }\n        defer { close(srcFd) }\n\n        let dstFd = open(tempFile.path, O_WRONLY | O_CREAT | O_TRUNC, 0o644)\n        guard dstFd >= 0 else { throw ArchiveError.failedToDetectFormat }\n        defer { close(dstFd) }\n\n        guard zstd_decompress_fd(srcFd, dstFd) == 0 else {\n            throw ArchiveError.failedToDetectFormat\n        }\n        return tempFile\n    }\n\n    deinit {\n        archive_read_free(underlying)\n        try? fileHandle?.close()\n\n        // Clean up temp decompressed file\n        if let tempFile = tempDecompressedFile {\n            try? FileManager.default.removeItem(at: tempFile.deletingLastPathComponent())\n        }\n    }\n}\n\nextension CInt {\n    fileprivate func checkOk(elseThrow error: @autoclosure () -> ArchiveError) throws {\n        guard self == ARCHIVE_OK else { throw error() }\n    }\n    fileprivate func checkOk(elseThrow error: (CInt) -> ArchiveError) throws {\n        guard self == ARCHIVE_OK else { throw error(self) }\n    }\n\n}\n\nextension ArchiveReader: Sequence {\n    public func makeIterator() -> Iterator {\n        Iterator(reader: self)\n    }\n\n    public struct Iterator: IteratorProtocol {\n        var reader: ArchiveReader\n\n        public mutating func next() -> (WriteEntry, Data)? {\n            let entry = WriteEntry()\n            let result = archive_read_next_header2(reader.underlying, entry.underlying)\n            if result == ARCHIVE_EOF {\n                return nil\n            }\n            let data = reader.readDataForEntry(entry)\n            return (entry, data)\n        }\n    }\n\n    /// Returns an iterator that yields archive entries.\n    public func makeStreamingIterator() -> StreamingIterator {\n        StreamingIterator(reader: self)\n    }\n\n    public struct StreamingIterator: Sequence, IteratorProtocol {\n        var reader: ArchiveReader\n\n        public func makeIterator() -> StreamingIterator {\n            self\n        }\n\n        public mutating func next() -> (WriteEntry, ArchiveEntryReader)? {\n            let entry = WriteEntry()\n            let result = archive_read_next_header2(reader.underlying, entry.underlying)\n            if result == ARCHIVE_EOF {\n                return nil\n            }\n            let streamReader = ArchiveEntryReader(reader: reader)\n            return (entry, streamReader)\n        }\n    }\n\n    internal func readDataForEntry(_ entry: WriteEntry) -> Data {\n        let bufferSize = Int(Swift.min(entry.size ?? 4096, 4096))\n        var entry = Data()\n        var part = Data(count: bufferSize)\n        while true {\n            let c = part.withUnsafeMutableBytes { buffer in\n                guard let baseAddress = buffer.baseAddress else {\n                    return 0\n                }\n                return archive_read_data(self.underlying, baseAddress, buffer.count)\n            }\n            guard c > 0 else { break }\n            part.count = c\n            entry.append(part)\n        }\n        return entry\n    }\n}\n\nextension ArchiveReader {\n    public convenience init(name: String, bundle: Data, tempDirectoryBaseName: String? = nil) throws {\n        let baseName = tempDirectoryBaseName ?? \"Unarchiver\"\n        let url = createTemporaryDirectory(baseName: baseName)!.appendingPathComponent(name)\n        try bundle.write(to: url, options: .atomic)\n        try self.init(format: .zip, filter: .none, file: url)\n    }\n\n    /// Extracts the contents of an archive to the provided directory.\n    /// Rejects member paths that escape the root directory or traverse\n    /// symbolic links, and uses a \"last entry wins\" replacement policy\n    /// for an existing file at a path to be extracted.\n    public func extractContents(to directory: URL) throws -> [String] {\n        // Create the root directory with standard permissions\n        // and create a FileDescriptor for secure path traveral.\n        let fm = FileManager.default\n        let rootFilePath = FilePath(directory.path)\n        try fm.createDirectory(atPath: directory.path, withIntermediateDirectories: true)\n        let rootFileDescriptor = try FileDescriptor.open(rootFilePath, .readOnly)\n        defer { try? rootFileDescriptor.close() }\n\n        // Iterate and extract archive entries, collecting rejected paths.\n        var foundEntry = false\n        var rejectedPaths = [String]()\n        for (entry, dataReader) in self.makeStreamingIterator() {\n            guard let memberPath = (entry.path.map { FilePath($0) }) else {\n                continue\n            }\n            foundEntry = true\n\n            // Try to extract the entry, catching path validation errors\n            let extracted = try extractEntry(\n                entry: entry,\n                dataReader: dataReader,\n                memberPath: memberPath,\n                rootFileDescriptor: rootFileDescriptor\n            )\n\n            if !extracted {\n                rejectedPaths.append(memberPath.string)\n            }\n        }\n        guard foundEntry else {\n            throw ArchiveError.failedToExtractArchive(\"no entries found in archive\")\n        }\n\n        return rejectedPaths\n    }\n\n    /// This method extracts a given file from the archive.\n    /// This operation modifies the underlying file descriptor's position within the archive,\n    /// meaning subsequent reads will start from a new location.\n    /// To reset the underlying file descriptor to the beginning of the archive, close and\n    /// reopen the archive.\n    public func extractFile(path: String) throws -> (WriteEntry, Data) {\n        let entry = WriteEntry()\n        while archive_read_next_header2(self.underlying, entry.underlying) != ARCHIVE_EOF {\n            guard let entryPath = entry.path else { continue }\n            let trimCharSet = CharacterSet(charactersIn: \"./\")\n            let trimmedEntry = entryPath.trimmingCharacters(in: trimCharSet)\n            let trimmedRequired = path.trimmingCharacters(in: trimCharSet)\n            guard trimmedEntry == trimmedRequired else { continue }\n            let data = readDataForEntry(entry)\n            return (entry, data)\n        }\n        throw ArchiveError.failedToExtractArchive(\" \\(path) not found in archive\")\n    }\n\n    /// Extracts a single archive entry.\n    /// Returns false if the entry was rejected due to path validation errors.\n    /// Throws on system errors.\n    private func extractEntry(\n        entry: WriteEntry,\n        dataReader: ArchiveEntryReader,\n        memberPath: FilePath,\n        rootFileDescriptor: FileDescriptor\n    ) throws -> Bool {\n        guard let lastComponent = memberPath.lastComponent else {\n            return false\n        }\n        let relativePath = memberPath.removingLastComponent()\n        let type = entry.fileType\n\n        do {\n            switch type {\n            case .regular:\n                try rootFileDescriptor.mkdirSecure(relativePath, makeIntermediates: true) { fd in\n                    // Remove existing entry if present (mimics containerd's \"last entry wins\" behavior)\n                    try? fd.unlinkRecursiveSecure(filename: lastComponent)\n\n                    // Open file for writing using openat with O_NOFOLLOW to prevent TOC-TOU attacks\n                    let fileMode = entry.permissions & 0o777  // Mask to permission bits only\n                    let fileFd = openat(fd.rawValue, lastComponent.string, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, fileMode)\n                    guard fileFd >= 0 else {\n                        throw ArchiveError.failedToExtractArchive(\"failed to create file: \\(memberPath)\")\n                    }\n                    defer { close(fileFd) }\n\n                    try Self.copyDataReaderToFd(dataReader: dataReader, fileFd: fileFd, memberPath: memberPath)\n                    setFileAttributes(fd: fileFd, entry: entry)\n                }\n            case .directory:\n                try rootFileDescriptor.mkdirSecure(memberPath, makeIntermediates: true) { fd in\n                    setFileAttributes(fd: fd.rawValue, entry: entry)\n                }\n            case .symbolicLink:\n                guard let targetPath = (entry.symlinkTarget.map { FilePath($0) }) else {\n                    return false\n                }\n                var symlinkCreated = false\n                try rootFileDescriptor.mkdirSecure(relativePath, makeIntermediates: true) { fd in\n                    // Remove existing entry if present (mimics containerd's \"last entry wins\" behavior)\n                    try? fd.unlinkRecursiveSecure(filename: lastComponent)\n\n                    guard symlinkat(targetPath.string, fd.rawValue, lastComponent.string) == 0 else {\n                        throw ArchiveError.failedToExtractArchive(\"failed to create symlink: \\(targetPath) <- \\(memberPath)\")\n                    }\n                    symlinkCreated = true\n                }\n                return symlinkCreated\n            default:\n                return false\n            }\n\n            return true\n        } catch let error as SecurePathError {\n            // Just reject path validation errors, don't fail the extraction\n            switch error {\n            case .systemError:\n                // Fail for system errors\n                throw error\n            case .invalidRelativePath, .invalidPathComponent, .cannotFollowSymlink:\n                return false\n            }\n        }\n    }\n\n    private func setFileAttributes(fd: Int32, entry: WriteEntry) {\n        fchmod(fd, entry.permissions)\n        if let owner = entry.owner, let group = entry.group {\n            fchown(fd, owner, group)\n        }\n    }\n\n    private static func copyDataReaderToFd(dataReader: ArchiveEntryReader, fileFd: Int32, memberPath: FilePath) throws {\n        var buffer = [UInt8](repeating: 0, count: ArchiveReader.chunkSize)\n        while true {\n            let bytesRead = buffer.withUnsafeMutableBufferPointer { bufferPtr in\n                guard let baseAddress = bufferPtr.baseAddress else { return 0 }\n                return dataReader.read(baseAddress, maxLength: bufferPtr.count)\n            }\n\n            if bytesRead < 0 {\n                throw ArchiveError.failedToExtractArchive(\"failed to read data for: \\(memberPath)\")\n            }\n            if bytesRead == 0 {\n                break  // EOF\n            }\n\n            let bytesWritten = write(fileFd, buffer, bytesRead)\n            guard bytesWritten == bytesRead else {\n                throw ArchiveError.failedToExtractArchive(\"failed to write data for: \\(memberPath)\")\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationArchive/ArchiveWriter.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport CArchive\nimport Foundation\nimport SystemPackage\n\n/// A class responsible for writing archives in various formats.\npublic final class ArchiveWriter {\n    private static let chunkSize = 4 * 1024 * 1024\n\n    var underlying: OpaquePointer!\n\n    /// Initialize a new `ArchiveWriter` with the given configuration.\n    /// This method attempts to initialize an empty archive in memory, failing which it throws a `unableToCreateArchive` error.\n    public init(configuration: ArchiveWriterConfiguration) throws {\n        // because for some bizarre reason, UTF8 paths won't work unless this process explicitly sets a locale like en_US.UTF-8\n        try Self.attemptSetLocales(locales: configuration.locales)\n\n        guard let underlying = archive_write_new() else { throw ArchiveError.unableToCreateArchive }\n        self.underlying = underlying\n\n        try setFormat(configuration.format)\n        try addFilter(configuration.filter)\n        try setOptions(configuration.options)\n    }\n\n    /// Initialize a new `ArchiveWriter` for writing into the specified file with the given configuration options.\n    public convenience init(format: Format, filter: Filter, options: [Options] = [], locales: [String] = ArchiveWriterConfiguration.defaultLocales, file: URL) throws {\n        let config = ArchiveWriterConfiguration(\n            format: format,\n            filter: filter,\n            options: options,\n            locales: locales\n        )\n        try self.init(configuration: config)\n        try self.open(file: file)\n    }\n\n    /// Opens the given file for writing data into\n    public func open(file: URL) throws {\n        guard let underlying = underlying else { throw ArchiveError.noUnderlyingArchive }\n        let res = archive_write_open_filename(underlying, file.path)\n        try wrap(res, ArchiveError.unableToOpenArchive, underlying: underlying)\n    }\n\n    /// Opens the given fd for writing data into\n    public func open(fileDescriptor: Int32) throws {\n        guard let underlying = underlying else { throw ArchiveError.noUnderlyingArchive }\n        let res = archive_write_open_fd(underlying, fileDescriptor)\n        try wrap(res, ArchiveError.unableToOpenArchive, underlying: underlying)\n    }\n\n    /// Performs any necessary finalizations on the archive and releases resources.\n    public func finishEncoding() throws {\n        if let u = underlying {\n            let r = archive_free(u)\n            do {\n                try wrap(r, ArchiveError.unableToCloseArchive, underlying: underlying)\n                underlying = nil\n            } catch {\n                underlying = nil\n                throw error\n            }\n        }\n    }\n\n    deinit {\n        if let u = underlying {\n            archive_free(u)\n            underlying = nil\n        }\n    }\n\n    private static func attemptSetLocales(locales: [String]) throws {\n        for locale in locales {\n            if setlocale(LC_ALL, locale) != nil {\n                return\n            }\n        }\n        throw ArchiveError.failedToSetLocale(locales: locales)\n    }\n}\n\npublic class ArchiveWriterTransaction {\n    private let writer: ArchiveWriter\n\n    fileprivate init(writer: ArchiveWriter) {\n        self.writer = writer\n    }\n\n    public func writeHeader(entry: WriteEntry) throws {\n        try writer.writeHeader(entry: entry)\n    }\n\n    public func writeChunk(data: UnsafeRawBufferPointer) throws {\n        try writer.writeData(data: data)\n    }\n\n    public func finish() throws {\n        try writer.finishEntry()\n    }\n}\n\nextension ArchiveWriter {\n    public func makeTransactionWriter() -> ArchiveWriterTransaction {\n        ArchiveWriterTransaction(writer: self)\n    }\n\n    /// Create a new entry in the archive with the given properties.\n    /// - Parameters:\n    ///   - entry: A `WriteEntry` object describing the metadata of the entry to be created\n    ///            (e.g., name, modification date, permissions).\n    ///   - data: The `Data` object containing the content for the new entry.\n    public func writeEntry(entry: WriteEntry, data: Data) throws {\n        try data.withUnsafeBytes { bytes in\n            try writeEntry(entry: entry, data: bytes)\n        }\n    }\n\n    /// Creates a new entry in the archive with the given properties.\n    ///\n    /// This method performs the following:\n    /// 1. Writes the archive header using the provided `WriteEntry` metadata.\n    /// 2. Writes the content from the `UnsafeRawBufferPointer` into the archive.\n    /// 3. Finalizes the entry in the archive.\n    ///\n    /// - Parameters:\n    ///   - entry: A `WriteEntry` object describing the metadata of the entry to be created\n    ///            (e.g., name, modification date, permissions, type).\n    ///   - data: An optional `UnsafeRawBufferPointer` containing the raw bytes for the new entry's\n    ///           content. Pass `nil` for entries that do not have content data (e.g., directories, symlinks).\n    public func writeEntry(entry: WriteEntry, data: UnsafeRawBufferPointer?) throws {\n        try writeHeader(entry: entry)\n        if let data = data {\n            try writeData(data: data)\n        }\n        try finishEntry()\n    }\n\n    fileprivate func writeHeader(entry: WriteEntry) throws {\n        guard let underlying = self.underlying else { throw ArchiveError.noUnderlyingArchive }\n\n        try wrap(\n            archive_write_header(underlying, entry.underlying), ArchiveError.unableToWriteEntryHeader,\n            underlying: underlying)\n    }\n\n    fileprivate func finishEntry() throws {\n        guard let underlying = self.underlying else { throw ArchiveError.noUnderlyingArchive }\n\n        archive_write_finish_entry(underlying)\n    }\n\n    fileprivate func writeData(data: UnsafeRawBufferPointer) throws {\n        guard let underlying = self.underlying else { throw ArchiveError.noUnderlyingArchive }\n\n        var offset = 0\n        while offset < data.count {\n            guard let baseAddress = data.baseAddress?.advanced(by: offset) else {\n                throw ArchiveError.invalidBaseAddressArchiveWrite\n            }\n            let result = archive_write_data(underlying, baseAddress, data.count - offset)\n            guard result > 0 else {\n                throw ArchiveError.unableToWriteData(result)\n            }\n            offset += Int(result)\n        }\n    }\n}\n\nextension ArchiveWriter {\n    /// Recursively archives the content of a directory. Regular files, symlinks and directories are added into the archive.\n    /// Note: Symlinks are added to the archive if both the source and target for the symlink are both contained in the top level directory.\n    public func archiveDirectory(_ dir: URL) throws {\n        let fm = FileManager.default\n        let dirPath = FilePath(dir.path)\n\n        guard let enumerator = fm.enumerator(atPath: dirPath.string) else {\n            throw POSIXError(.ENOTDIR)\n        }\n\n        // Emit a leading \"./\" entry for the root directory, matching GNU/BSD tar behavior.\n        var rootStat = stat()\n        guard lstat(dirPath.string, &rootStat) == 0 else {\n            let err = POSIXErrorCode(rawValue: errno) ?? .EINVAL\n            throw ArchiveError.failedToCreateArchive(\"lstat failed for '\\(dirPath)': \\(POSIXError(err))\")\n        }\n        let rootEntry = WriteEntry()\n        rootEntry.path = \"./\"\n        rootEntry.size = 0\n        rootEntry.fileType = .directory\n        rootEntry.owner = rootStat.st_uid\n        rootEntry.group = rootStat.st_gid\n        rootEntry.permissions = rootStat.st_mode\n        #if os(macOS)\n        rootEntry.creationDate = Date(timeIntervalSince1970: Double(rootStat.st_ctimespec.tv_sec))\n        rootEntry.contentAccessDate = Date(timeIntervalSince1970: Double(rootStat.st_atimespec.tv_sec))\n        rootEntry.modificationDate = Date(timeIntervalSince1970: Double(rootStat.st_mtimespec.tv_sec))\n        #else\n        rootEntry.creationDate = Date(timeIntervalSince1970: Double(rootStat.st_ctim.tv_sec))\n        rootEntry.contentAccessDate = Date(timeIntervalSince1970: Double(rootStat.st_atim.tv_sec))\n        rootEntry.modificationDate = Date(timeIntervalSince1970: Double(rootStat.st_mtim.tv_sec))\n        #endif\n        try self.writeHeader(entry: rootEntry)\n\n        for case let relativePath as String in enumerator {\n            let fullPath = dirPath.appending(relativePath)\n\n            var statInfo = stat()\n            guard lstat(fullPath.string, &statInfo) == 0 else {\n                let errNo = errno\n                let err = POSIXErrorCode(rawValue: errNo) ?? .EINVAL\n                throw ArchiveError.failedToCreateArchive(\"lstat failed for '\\(fullPath)': \\(POSIXError(err))\")\n            }\n\n            let mode = statInfo.st_mode\n            let uid = statInfo.st_uid\n            let gid = statInfo.st_gid\n            var size: Int64 = 0\n            let type: URLFileResourceType\n\n            if (mode & S_IFMT) == S_IFREG {\n                type = .regular\n                size = Int64(statInfo.st_size)\n            } else if (mode & S_IFMT) == S_IFDIR {\n                type = .directory\n            } else if (mode & S_IFMT) == S_IFLNK {\n                type = .symbolicLink\n            } else {\n                continue\n            }\n\n            #if os(macOS)\n            let created = Date(timeIntervalSince1970: Double(statInfo.st_ctimespec.tv_sec))\n            let access = Date(timeIntervalSince1970: Double(statInfo.st_atimespec.tv_sec))\n            let modified = Date(timeIntervalSince1970: Double(statInfo.st_mtimespec.tv_sec))\n            #else\n            let created = Date(timeIntervalSince1970: Double(statInfo.st_ctim.tv_sec))\n            let access = Date(timeIntervalSince1970: Double(statInfo.st_atim.tv_sec))\n            let modified = Date(timeIntervalSince1970: Double(statInfo.st_mtim.tv_sec))\n            #endif\n\n            let entry = WriteEntry()\n            if type == .symbolicLink {\n                let targetPath = try fm.destinationOfSymbolicLink(atPath: fullPath.string)\n                // Resolve the target relative to the symlink's parent, not the archive root.\n                let symlinkParent = fullPath.removingLastComponent()\n                let resolvedFull = symlinkParent.appending(targetPath).lexicallyNormalized()\n                guard resolvedFull.starts(with: dirPath) else {\n                    continue\n                }\n                entry.symlinkTarget = targetPath\n            }\n\n            entry.path = relativePath\n            entry.size = size\n            entry.creationDate = created\n            entry.modificationDate = modified\n            entry.contentAccessDate = access\n            entry.fileType = type\n            entry.group = gid\n            entry.owner = uid\n            entry.permissions = mode\n            if type == .regular {\n                let buf = UnsafeMutableRawBufferPointer.allocate(byteCount: Self.chunkSize, alignment: 1)\n                guard let baseAddress = buf.baseAddress else {\n                    throw ArchiveError.failedToCreateArchive(\"cannot create temporary buffer of size \\(Self.chunkSize)\")\n                }\n                defer { buf.deallocate() }\n                let fd = Foundation.open(fullPath.string, O_RDONLY)\n                guard fd >= 0 else {\n                    let err = POSIXErrorCode(rawValue: errno) ?? .EINVAL\n                    throw ArchiveError.failedToCreateArchive(\"cannot open file \\(fullPath.string) for reading: \\(err)\")\n                }\n                defer { close(fd) }\n                try self.writeHeader(entry: entry)\n                while true {\n                    let n = read(fd, baseAddress, Self.chunkSize)\n                    if n == 0 { break }\n                    if n < 0 {\n                        let err = POSIXErrorCode(rawValue: errno) ?? .EIO\n                        throw ArchiveError.failedToCreateArchive(\"failed to read from file \\(fullPath.string): \\(err)\")\n                    }\n                    try self.writeData(data: UnsafeRawBufferPointer(start: baseAddress, count: n))\n                }\n                try self.finishEntry()\n            } else {\n                try self.writeEntry(entry: entry, data: nil)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationArchive/ArchiveWriterConfiguration.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport CArchive\n\n/// Represents the configuration settings for an `ArchiveWriter`.\n///\n/// This struct allows specifying the archive format, compression filter,\n/// various format-specific options, and preferred locales for string encoding.\npublic struct ArchiveWriterConfiguration {\n    public static let defaultLocales = [\"en_US.UTF-8\", \"C.UTF-8\"]\n\n    /// The desired archive format\n    public var format: Format\n    /// The compression filter to apply to the archive\n    public var filter: Filter\n    /// An array of format-specific options to apply to the archive.\n    /// This includes options like compression level and extended attribute format.\n    public var options: [Options]\n    /// An array of preferred locale identifiers for string encoding\n    public var locales: [String]\n\n    /// Initializes a new `ArchiveWriterConfiguration`.\n    ///\n    /// Sets up the configuration with the specified format, filter, options, and locales.\n    public init(\n        format: Format, filter: Filter, options: [Options] = [], locales: [String] = Self.defaultLocales\n    ) {\n        self.format = format\n        self.filter = filter\n        self.options = options\n        self.locales = locales\n    }\n}\n\nextension ArchiveWriter {\n    internal func setFormat(_ format: Format) throws {\n        guard let underlying = self.underlying else { throw ArchiveError.noUnderlyingArchive }\n        let r = archive_write_set_format(underlying, format.code)\n        guard r == ARCHIVE_OK else { throw ArchiveError.unableToSetFormat(r, format) }\n    }\n\n    internal func addFilter(_ filter: Filter) throws {\n        guard let underlying = self.underlying else { throw ArchiveError.noUnderlyingArchive }\n        let r = archive_write_add_filter(underlying, filter.code)\n        guard r == ARCHIVE_OK else { throw ArchiveError.unableToAddFilter(r, filter) }\n    }\n\n    internal func setOptions(_ options: [Options]) throws {\n        try options.forEach {\n            switch $0 {\n            case .compressionLevel(let level):\n                try wrap(\n                    archive_write_set_option(underlying, nil, \"compression-level\", \"\\(level)\"),\n                    ArchiveError.unableToSetOption, underlying: self.underlying)\n            case .compression(.store):\n                try wrap(\n                    archive_write_set_option(underlying, nil, \"compression\", \"store\"), ArchiveError.unableToSetOption,\n                    underlying: self.underlying)\n            case .compression(.deflate):\n                try wrap(\n                    archive_write_set_option(underlying, nil, \"compression\", \"deflate\"), ArchiveError.unableToSetOption,\n                    underlying: self.underlying)\n            case .xattrformat(let value):\n                let v = value.description\n                try wrap(\n                    archive_write_set_option(underlying, nil, \"xattrheader\", v), ArchiveError.unableToSetOption,\n                    underlying: self.underlying)\n            }\n        }\n    }\n}\n\npublic enum Options {\n    case compressionLevel(UInt32)\n    case compression(Compression)\n    case xattrformat(XattrFormat)\n\n    public enum Compression {\n        case store\n        case deflate\n    }\n\n    public enum XattrFormat: String, CustomStringConvertible {\n        case schily\n        case libarchive\n        case all\n\n        public var description: String {\n            switch self {\n            case .libarchive:\n                return \"LIBARCHIVE\"\n            case .schily:\n                return \"SCHILY\"\n            case .all:\n                return \"ALL\"\n            }\n        }\n    }\n}\n\n/// An enumeration of the supported archive formats.\npublic enum Format: String, Sendable {\n    /// POSIX-standard `ustar` archives\n    case ustar\n    case gnutar\n    /// POSIX `pax interchange format` archives\n    case pax\n    case paxRestricted\n    /// POSIX octet-oriented cpio archives\n    case cpio\n    case cpioNewc\n    /// Zip archive\n    case zip\n    /// two different variants of shar archives\n    case shar\n    case sharDump\n    /// ISO9660 CD images\n    case iso9660\n    /// 7-Zip archives\n    case sevenZip\n    /// ar archives\n    case arBSD\n    case arGNU\n    /// mtree file tree descriptions\n    case mtree\n    /// XAR archives\n    case xar\n\n    internal var code: CInt {\n        switch self {\n        case .ustar: return ARCHIVE_FORMAT_TAR_USTAR\n        case .pax: return ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE\n        case .paxRestricted: return ARCHIVE_FORMAT_TAR_PAX_RESTRICTED\n        case .gnutar: return ARCHIVE_FORMAT_TAR_GNUTAR\n        case .cpio: return ARCHIVE_FORMAT_CPIO_POSIX\n        case .cpioNewc: return ARCHIVE_FORMAT_CPIO_AFIO_LARGE\n        case .zip: return ARCHIVE_FORMAT_ZIP\n        case .shar: return ARCHIVE_FORMAT_SHAR_BASE\n        case .sharDump: return ARCHIVE_FORMAT_SHAR_DUMP\n        case .iso9660: return ARCHIVE_FORMAT_ISO9660\n        case .sevenZip: return ARCHIVE_FORMAT_7ZIP\n        case .arBSD: return ARCHIVE_FORMAT_AR_BSD\n        case .arGNU: return ARCHIVE_FORMAT_AR_GNU\n        case .mtree: return ARCHIVE_FORMAT_MTREE\n        case .xar: return ARCHIVE_FORMAT_XAR\n        }\n    }\n}\n\n/// An enumeration of the supported filters (compression / encoding standards) for an archive.\npublic enum Filter: String, Sendable {\n    case none\n    case gzip\n    case bzip2\n    case compress\n    case lzma\n    case xz\n    case uu\n    case rpm\n    case lzip\n    case lrzip\n    case lzop\n    case grzip\n    case lz4\n    case zstd\n\n    internal var code: CInt {\n        switch self {\n        case .none: return ARCHIVE_FILTER_NONE\n        case .gzip: return ARCHIVE_FILTER_GZIP\n        case .bzip2: return ARCHIVE_FILTER_BZIP2\n        case .compress: return ARCHIVE_FILTER_COMPRESS\n        case .lzma: return ARCHIVE_FILTER_LZMA\n        case .xz: return ARCHIVE_FILTER_XZ\n        case .uu: return ARCHIVE_FILTER_UU\n        case .rpm: return ARCHIVE_FILTER_RPM\n        case .lzip: return ARCHIVE_FILTER_LZIP\n        case .lrzip: return ARCHIVE_FILTER_LRZIP\n        case .lzop: return ARCHIVE_FILTER_LZOP\n        case .grzip: return ARCHIVE_FILTER_GRZIP\n        case .lz4: return ARCHIVE_FILTER_LZ4\n        case .zstd: return ARCHIVE_FILTER_ZSTD\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationArchive/CArchive/COPYING",
    "content": "The libarchive distribution as a whole is Copyright by Tim Kientzle\nand is subject to the copyright notice reproduced at the bottom of\nthis file.\n\nEach individual file in this distribution should have a clear\ncopyright/licensing statement at the beginning of the file.  If any do\nnot, please let me know and I will rectify it.  The following is\nintended to summarize the copyright status of the individual files;\nthe actual statements in the files are controlling.\n\n* Except as listed below, all C sources (including .c and .h files)\n  and documentation files are subject to the copyright notice reproduced\n  at the bottom of this file.\n\n* The following source files are also subject in whole or in part to\n  a 3-clause UC Regents copyright; please read the individual source\n  files for details:\n   libarchive/archive_read_support_filter_compress.c\n   libarchive/archive_write_add_filter_compress.c\n   libarchive/mtree.5\n\n* The following source files are in the public domain:\n   libarchive/archive_getdate.c\n\n* The following source files are triple-licensed with the ability to choose\n  from CC0 1.0 Universal, OpenSSL or Apache 2.0 licenses:\n   libarchive/archive_blake2.h\n   libarchive/archive_blake2_impl.h\n   libarchive/archive_blake2s_ref.c\n   libarchive/archive_blake2sp_ref.c\n\n* The build files---including Makefiles, configure scripts,\n  and auxiliary scripts used as part of the compile process---have\n  widely varying licensing terms.  Please check individual files before\n  distributing them to see if those restrictions apply to you.\n\nI intend for all new source code to use the license below and hope over\ntime to replace code with other licenses with new implementations that\ndo use the license below.  The varying licensing of the build scripts\nseems to be an unavoidable mess.\n\n\nCopyright (c) 2003-2018 <author(s)>\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n1. Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer\n   in this position and unchanged.\n2. Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in the\n   documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR\nIMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\nOF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\nIN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\nNOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\nTHIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "Sources/ContainerizationArchive/CArchive/archive_swift_bridge.c",
    "content": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"archive_bridge.h\"\n#include <zstd.h>\n#include <stdlib.h>\n#include <unistd.h>\n\nvoid archive_set_error_wrapper(struct archive *a, int error_number, const char *error_string) {\n    archive_set_error(a, error_number, \"%s\", error_string);\n}\n\nint zstd_decompress_fd(int src_fd, int dst_fd) {\n    ZSTD_DStream *dstream = ZSTD_createDStream();\n    if (!dstream) return 1;\n\n    size_t init_result = ZSTD_initDStream(dstream);\n    if (ZSTD_isError(init_result)) {\n        ZSTD_freeDStream(dstream);\n        return 1;\n    }\n\n    size_t in_size = ZSTD_DStreamInSize();\n    size_t out_size = ZSTD_DStreamOutSize();\n    void *in_buf = malloc(in_size);\n    void *out_buf = malloc(out_size);\n    if (!in_buf || !out_buf) {\n        free(in_buf);\n        free(out_buf);\n        ZSTD_freeDStream(dstream);\n        return 1;\n    }\n\n    int rc = 0;\n    ssize_t bytes_read;\n    while ((bytes_read = read(src_fd, in_buf, in_size)) > 0) {\n        ZSTD_inBuffer input = { in_buf, (size_t)bytes_read, 0 };\n        while (input.pos < input.size) {\n            ZSTD_outBuffer output = { out_buf, out_size, 0 };\n            size_t result = ZSTD_decompressStream(dstream, &output, &input);\n            if (ZSTD_isError(result)) { rc = 1; goto done; }\n            if (output.pos > 0) {\n                ssize_t written = write(dst_fd, out_buf, output.pos);\n                if (written != (ssize_t)output.pos) { rc = 1; goto done; }\n            }\n        }\n    }\n    if (bytes_read < 0) rc = 1;\n\ndone:\n    free(in_buf);\n    free(out_buf);\n    ZSTD_freeDStream(dstream);\n    return rc;\n}\n"
  },
  {
    "path": "Sources/ContainerizationArchive/CArchive/include/archive.h",
    "content": "/*-\n * Copyright (c) 2003-2010 Tim Kientzle\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef ARCHIVE_H_INCLUDED\n#define\tARCHIVE_H_INCLUDED\n\n/*\n * The version number is expressed as a single integer that makes it\n * easy to compare versions at build time: for version a.b.c, the\n * version number is printf(\"%d%03d%03d\",a,b,c).  For example, if you\n * know your application requires version 2.12.108 or later, you can\n * assert that ARCHIVE_VERSION_NUMBER >= 2012108.\n */\n/* Note: Compiler will complain if this does not match archive_entry.h! */\n#define\tARCHIVE_VERSION_NUMBER 3007007\n\n#include <sys/stat.h>\n#include <stddef.h>  /* for wchar_t */\n#include <stdio.h> /* For FILE * */\n#include <time.h> /* For time_t */\n\n/*\n * Note: archive.h is for use outside of libarchive; the configuration\n * headers (config.h, archive_platform.h, etc.) are purely internal.\n * Do NOT use HAVE_XXX configuration macros to control the behavior of\n * this header!  If you must conditionalize, use predefined compiler and/or\n * platform macros.\n */\n#if defined(__BORLANDC__) && __BORLANDC__ >= 0x560\n# include <stdint.h>\n#elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__) && !defined(_SCO_DS) && !defined(__osf__) && !defined(__CLANG_INTTYPES_H)\n# include <inttypes.h>\n#endif\n\n/* Get appropriate definitions of 64-bit integer */\n#if !defined(__LA_INT64_T_DEFINED)\n/* Older code relied on the __LA_INT64_T macro; after 4.0 we'll switch to the typedef exclusively. */\n# if ARCHIVE_VERSION_NUMBER < 4000000\n#define __LA_INT64_T la_int64_t\n# endif\n#define __LA_INT64_T_DEFINED\n# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)\ntypedef __int64 la_int64_t;\n# else\n# include <unistd.h>  /* ssize_t */\n#  if defined(_SCO_DS) || defined(__osf__)\ntypedef long long la_int64_t;\n#  else\ntypedef int64_t la_int64_t;\n#  endif\n# endif\n#endif\n\n/* The la_ssize_t should match the type used in 'struct stat' */\n#if !defined(__LA_SSIZE_T_DEFINED)\n/* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */\n# if ARCHIVE_VERSION_NUMBER < 4000000\n#define __LA_SSIZE_T la_ssize_t\n# endif\n#define __LA_SSIZE_T_DEFINED\n# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)\n#  if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)\ntypedef ssize_t la_ssize_t;\n#  elif defined(_WIN64)\ntypedef __int64 la_ssize_t;\n#  else\ntypedef long la_ssize_t;\n#  endif\n# else\n# include <unistd.h>  /* ssize_t */\ntypedef ssize_t la_ssize_t;\n# endif\n#endif\n\n/* Large file support for Android */\n#if defined(__LIBARCHIVE_BUILD) && defined(__ANDROID__)\n#include \"android_lf.h\"\n#endif\n\n/*\n * On Windows, define LIBARCHIVE_STATIC if you're building or using a\n * .lib.  The default here assumes you're building a DLL.  Only\n * libarchive source should ever define __LIBARCHIVE_BUILD.\n */\n#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC)\n# ifdef __LIBARCHIVE_BUILD\n#  ifdef __GNUC__\n#   define __LA_DECL\t__attribute__((dllexport)) extern\n#  else\n#   define __LA_DECL\t__declspec(dllexport)\n#  endif\n# else\n#  ifdef __GNUC__\n#   define __LA_DECL\n#  else\n#   define __LA_DECL\t__declspec(dllimport)\n#  endif\n# endif\n#elif defined __LIBARCHIVE_ENABLE_VISIBILITY\n#  define __LA_DECL __attribute__((visibility(\"default\")))\n#else\n/* Static libraries or non-Windows needs no special declaration. */\n# define __LA_DECL\n#endif\n\n#if defined(__GNUC__) && __GNUC__ >= 3 && !defined(__MINGW32__)\n#define\t__LA_PRINTF(fmtarg, firstvararg) \\\n\t__attribute__((__format__ (__printf__, fmtarg, firstvararg)))\n#else\n#define\t__LA_PRINTF(fmtarg, firstvararg)\t/* nothing */\n#endif\n\n#if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1\n# define __LA_DEPRECATED __attribute__((deprecated))\n#else\n# define __LA_DEPRECATED\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * The version number is provided as both a macro and a function.\n * The macro identifies the installed header; the function identifies\n * the library version (which may not be the same if you're using a\n * dynamically-linked version of the library).  Of course, if the\n * header and library are very different, you should expect some\n * strangeness.  Don't do that.\n */\n__LA_DECL int\t\tarchive_version_number(void);\n\n/*\n * Textual name/version of the library, useful for version displays.\n */\n#define\tARCHIVE_VERSION_ONLY_STRING \"3.7.7\"\n#define\tARCHIVE_VERSION_STRING \"libarchive \" ARCHIVE_VERSION_ONLY_STRING\n__LA_DECL const char *\tarchive_version_string(void);\n\n/*\n * Detailed textual name/version of the library and its dependencies.\n * This has the form:\n *    \"libarchive x.y.z zlib/a.b.c liblzma/d.e.f ... etc ...\"\n * the list of libraries described here will vary depending on how\n * libarchive was compiled.\n */\n__LA_DECL const char *\tarchive_version_details(void);\n\n/*\n * Returns NULL if libarchive was compiled without the associated library.\n * Otherwise, returns the version number that libarchive was compiled\n * against.\n */\n__LA_DECL const char *  archive_zlib_version(void);\n__LA_DECL const char *  archive_liblzma_version(void);\n__LA_DECL const char *  archive_bzlib_version(void);\n__LA_DECL const char *  archive_liblz4_version(void);\n__LA_DECL const char *  archive_libzstd_version(void);\n\n/* Declare our basic types. */\nstruct archive;\nstruct archive_entry;\n\n/*\n * Error codes: Use archive_errno() and archive_error_string()\n * to retrieve details.  Unless specified otherwise, all functions\n * that return 'int' use these codes.\n */\n#define\tARCHIVE_EOF\t  1\t/* Found end of archive. */\n#define\tARCHIVE_OK\t  0\t/* Operation was successful. */\n#define\tARCHIVE_RETRY\t(-10)\t/* Retry might succeed. */\n#define\tARCHIVE_WARN\t(-20)\t/* Partial success. */\n/* For example, if write_header \"fails\", then you can't push data. */\n#define\tARCHIVE_FAILED\t(-25)\t/* Current operation cannot complete. */\n/* But if write_header is \"fatal,\" then this archive is dead and useless. */\n#define\tARCHIVE_FATAL\t(-30)\t/* No more operations are possible. */\n\n/*\n * As far as possible, archive_errno returns standard platform errno codes.\n * Of course, the details vary by platform, so the actual definitions\n * here are stored in \"archive_platform.h\".  The symbols are listed here\n * for reference; as a rule, clients should not need to know the exact\n * platform-dependent error code.\n */\n/* Unrecognized or invalid file format. */\n/* #define\tARCHIVE_ERRNO_FILE_FORMAT */\n/* Illegal usage of the library. */\n/* #define\tARCHIVE_ERRNO_PROGRAMMER_ERROR */\n/* Unknown or unclassified error. */\n/* #define\tARCHIVE_ERRNO_MISC */\n\n/*\n * Callbacks are invoked to automatically read/skip/write/open/close the\n * archive. You can provide your own for complex tasks (like breaking\n * archives across multiple tapes) or use standard ones built into the\n * library.\n */\n\n/* Returns pointer and size of next block of data from archive. */\ntypedef la_ssize_t\tarchive_read_callback(struct archive *,\n\t\t\t    void *_client_data, const void **_buffer);\n\n/* Skips at most request bytes from archive and returns the skipped amount.\n * This may skip fewer bytes than requested; it may even skip zero bytes.\n * If you do skip fewer bytes than requested, libarchive will invoke your\n * read callback and discard data as necessary to make up the full skip.\n */\ntypedef la_int64_t\tarchive_skip_callback(struct archive *,\n\t\t\t    void *_client_data, la_int64_t request);\n\n/* Seeks to specified location in the file and returns the position.\n * Whence values are SEEK_SET, SEEK_CUR, SEEK_END from stdio.h.\n * Return ARCHIVE_FATAL if the seek fails for any reason.\n */\ntypedef la_int64_t\tarchive_seek_callback(struct archive *,\n    void *_client_data, la_int64_t offset, int whence);\n\n/* Returns size actually written, zero on EOF, -1 on error. */\ntypedef la_ssize_t\tarchive_write_callback(struct archive *,\n\t\t\t    void *_client_data,\n\t\t\t    const void *_buffer, size_t _length);\n\ntypedef int\tarchive_open_callback(struct archive *, void *_client_data);\n\ntypedef int\tarchive_close_callback(struct archive *, void *_client_data);\n\ntypedef int\tarchive_free_callback(struct archive *, void *_client_data);\n\n/* Switches from one client data object to the next/prev client data object.\n * This is useful for reading from different data blocks such as a set of files\n * that make up one large file.\n */\ntypedef int archive_switch_callback(struct archive *, void *_client_data1,\n\t\t\t    void *_client_data2);\n\n/*\n * Returns a passphrase used for encryption or decryption, NULL on nothing\n * to do and give it up.\n */\ntypedef const char *archive_passphrase_callback(struct archive *,\n\t\t\t    void *_client_data);\n\n/*\n * Codes to identify various stream filters.\n */\n#define\tARCHIVE_FILTER_NONE\t0\n#define\tARCHIVE_FILTER_GZIP\t1\n#define\tARCHIVE_FILTER_BZIP2\t2\n#define\tARCHIVE_FILTER_COMPRESS\t3\n#define\tARCHIVE_FILTER_PROGRAM\t4\n#define\tARCHIVE_FILTER_LZMA\t5\n#define\tARCHIVE_FILTER_XZ\t6\n#define\tARCHIVE_FILTER_UU\t7\n#define\tARCHIVE_FILTER_RPM\t8\n#define\tARCHIVE_FILTER_LZIP\t9\n#define\tARCHIVE_FILTER_LRZIP\t10\n#define\tARCHIVE_FILTER_LZOP\t11\n#define\tARCHIVE_FILTER_GRZIP\t12\n#define\tARCHIVE_FILTER_LZ4\t13\n#define\tARCHIVE_FILTER_ZSTD\t14\n\n#if ARCHIVE_VERSION_NUMBER < 4000000\n#define\tARCHIVE_COMPRESSION_NONE\tARCHIVE_FILTER_NONE\n#define\tARCHIVE_COMPRESSION_GZIP\tARCHIVE_FILTER_GZIP\n#define\tARCHIVE_COMPRESSION_BZIP2\tARCHIVE_FILTER_BZIP2\n#define\tARCHIVE_COMPRESSION_COMPRESS\tARCHIVE_FILTER_COMPRESS\n#define\tARCHIVE_COMPRESSION_PROGRAM\tARCHIVE_FILTER_PROGRAM\n#define\tARCHIVE_COMPRESSION_LZMA\tARCHIVE_FILTER_LZMA\n#define\tARCHIVE_COMPRESSION_XZ\t\tARCHIVE_FILTER_XZ\n#define\tARCHIVE_COMPRESSION_UU\t\tARCHIVE_FILTER_UU\n#define\tARCHIVE_COMPRESSION_RPM\t\tARCHIVE_FILTER_RPM\n#define\tARCHIVE_COMPRESSION_LZIP\tARCHIVE_FILTER_LZIP\n#define\tARCHIVE_COMPRESSION_LRZIP\tARCHIVE_FILTER_LRZIP\n#endif\n\n/*\n * Codes returned by archive_format.\n *\n * Top 16 bits identifies the format family (e.g., \"tar\"); lower\n * 16 bits indicate the variant.  This is updated by read_next_header.\n * Note that the lower 16 bits will often vary from entry to entry.\n * In some cases, this variation occurs as libarchive learns more about\n * the archive (for example, later entries might utilize extensions that\n * weren't necessary earlier in the archive; in this case, libarchive\n * will change the format code to indicate the extended format that\n * was used).  In other cases, it's because different tools have\n * modified the archive and so different parts of the archive\n * actually have slightly different formats.  (Both tar and cpio store\n * format codes in each entry, so it is quite possible for each\n * entry to be in a different format.)\n */\n#define\tARCHIVE_FORMAT_BASE_MASK\t\t0xff0000\n#define\tARCHIVE_FORMAT_CPIO\t\t\t0x10000\n#define\tARCHIVE_FORMAT_CPIO_POSIX\t\t(ARCHIVE_FORMAT_CPIO | 1)\n#define\tARCHIVE_FORMAT_CPIO_BIN_LE\t\t(ARCHIVE_FORMAT_CPIO | 2)\n#define\tARCHIVE_FORMAT_CPIO_BIN_BE\t\t(ARCHIVE_FORMAT_CPIO | 3)\n#define\tARCHIVE_FORMAT_CPIO_SVR4_NOCRC\t\t(ARCHIVE_FORMAT_CPIO | 4)\n#define\tARCHIVE_FORMAT_CPIO_SVR4_CRC\t\t(ARCHIVE_FORMAT_CPIO | 5)\n#define\tARCHIVE_FORMAT_CPIO_AFIO_LARGE\t\t(ARCHIVE_FORMAT_CPIO | 6)\n#define\tARCHIVE_FORMAT_CPIO_PWB\t\t\t(ARCHIVE_FORMAT_CPIO | 7)\n#define\tARCHIVE_FORMAT_SHAR\t\t\t0x20000\n#define\tARCHIVE_FORMAT_SHAR_BASE\t\t(ARCHIVE_FORMAT_SHAR | 1)\n#define\tARCHIVE_FORMAT_SHAR_DUMP\t\t(ARCHIVE_FORMAT_SHAR | 2)\n#define\tARCHIVE_FORMAT_TAR\t\t\t0x30000\n#define\tARCHIVE_FORMAT_TAR_USTAR\t\t(ARCHIVE_FORMAT_TAR | 1)\n#define\tARCHIVE_FORMAT_TAR_PAX_INTERCHANGE\t(ARCHIVE_FORMAT_TAR | 2)\n#define\tARCHIVE_FORMAT_TAR_PAX_RESTRICTED\t(ARCHIVE_FORMAT_TAR | 3)\n#define\tARCHIVE_FORMAT_TAR_GNUTAR\t\t(ARCHIVE_FORMAT_TAR | 4)\n#define\tARCHIVE_FORMAT_ISO9660\t\t\t0x40000\n#define\tARCHIVE_FORMAT_ISO9660_ROCKRIDGE\t(ARCHIVE_FORMAT_ISO9660 | 1)\n#define\tARCHIVE_FORMAT_ZIP\t\t\t0x50000\n#define\tARCHIVE_FORMAT_EMPTY\t\t\t0x60000\n#define\tARCHIVE_FORMAT_AR\t\t\t0x70000\n#define\tARCHIVE_FORMAT_AR_GNU\t\t\t(ARCHIVE_FORMAT_AR | 1)\n#define\tARCHIVE_FORMAT_AR_BSD\t\t\t(ARCHIVE_FORMAT_AR | 2)\n#define\tARCHIVE_FORMAT_MTREE\t\t\t0x80000\n#define\tARCHIVE_FORMAT_RAW\t\t\t0x90000\n#define\tARCHIVE_FORMAT_XAR\t\t\t0xA0000\n#define\tARCHIVE_FORMAT_LHA\t\t\t0xB0000\n#define\tARCHIVE_FORMAT_CAB\t\t\t0xC0000\n#define\tARCHIVE_FORMAT_RAR\t\t\t0xD0000\n#define\tARCHIVE_FORMAT_7ZIP\t\t\t0xE0000\n#define\tARCHIVE_FORMAT_WARC\t\t\t0xF0000\n#define\tARCHIVE_FORMAT_RAR_V5\t\t\t0x100000\n\n/*\n * Codes returned by archive_read_format_capabilities().\n *\n * This list can be extended with values between 0 and 0xffff.\n * The original purpose of this list was to let different archive\n * format readers expose their general capabilities in terms of\n * encryption.\n */\n#define ARCHIVE_READ_FORMAT_CAPS_NONE (0) /* no special capabilities */\n#define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA (1<<0)  /* reader can detect encrypted data */\n#define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA (1<<1)  /* reader can detect encryptable metadata (pathname, mtime, etc.) */\n\n/*\n * Codes returned by archive_read_has_encrypted_entries().\n *\n * In case the archive does not support encryption detection at all\n * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned. If the reader\n * for some other reason (e.g. not enough bytes read) cannot say if\n * there are encrypted entries, ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW\n * is returned.\n */\n#define ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED -2\n#define ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW -1\n\n/*-\n * Basic outline for reading an archive:\n *   1) Ask archive_read_new for an archive reader object.\n *   2) Update any global properties as appropriate.\n *      In particular, you'll certainly want to call appropriate\n *      archive_read_support_XXX functions.\n *   3) Call archive_read_open_XXX to open the archive\n *   4) Repeatedly call archive_read_next_header to get information about\n *      successive archive entries.  Call archive_read_data to extract\n *      data for entries of interest.\n *   5) Call archive_read_free to end processing.\n */\n__LA_DECL struct archive\t*archive_read_new(void);\n\n/*\n * The archive_read_support_XXX calls enable auto-detect for this\n * archive handle.  They also link in the necessary support code.\n * For example, if you don't want bzlib linked in, don't invoke\n * support_compression_bzip2().  The \"all\" functions provide the\n * obvious shorthand.\n */\n\n#if ARCHIVE_VERSION_NUMBER < 4000000\n__LA_DECL int archive_read_support_compression_all(struct archive *)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_read_support_compression_bzip2(struct archive *)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_read_support_compression_compress(struct archive *)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_read_support_compression_gzip(struct archive *)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_read_support_compression_lzip(struct archive *)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_read_support_compression_lzma(struct archive *)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_read_support_compression_none(struct archive *)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_read_support_compression_program(struct archive *,\n\t\t     const char *command) __LA_DEPRECATED;\n__LA_DECL int archive_read_support_compression_program_signature\n\t\t(struct archive *, const char *,\n\t\t const void * /* match */, size_t) __LA_DEPRECATED;\n\n__LA_DECL int archive_read_support_compression_rpm(struct archive *)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_read_support_compression_uu(struct archive *)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_read_support_compression_xz(struct archive *)\n\t\t__LA_DEPRECATED;\n#endif\n\n__LA_DECL int archive_read_support_filter_all(struct archive *);\n__LA_DECL int archive_read_support_filter_by_code(struct archive *, int);\n__LA_DECL int archive_read_support_filter_bzip2(struct archive *);\n__LA_DECL int archive_read_support_filter_compress(struct archive *);\n__LA_DECL int archive_read_support_filter_gzip(struct archive *);\n__LA_DECL int archive_read_support_filter_grzip(struct archive *);\n__LA_DECL int archive_read_support_filter_lrzip(struct archive *);\n__LA_DECL int archive_read_support_filter_lz4(struct archive *);\n__LA_DECL int archive_read_support_filter_lzip(struct archive *);\n__LA_DECL int archive_read_support_filter_lzma(struct archive *);\n__LA_DECL int archive_read_support_filter_lzop(struct archive *);\n__LA_DECL int archive_read_support_filter_none(struct archive *);\n__LA_DECL int archive_read_support_filter_program(struct archive *,\n\t\t     const char *command);\n__LA_DECL int archive_read_support_filter_program_signature\n\t\t(struct archive *, const char * /* cmd */,\n\t\t\t\t    const void * /* match */, size_t);\n__LA_DECL int archive_read_support_filter_rpm(struct archive *);\n__LA_DECL int archive_read_support_filter_uu(struct archive *);\n__LA_DECL int archive_read_support_filter_xz(struct archive *);\n__LA_DECL int archive_read_support_filter_zstd(struct archive *);\n\n__LA_DECL int archive_read_support_format_7zip(struct archive *);\n__LA_DECL int archive_read_support_format_all(struct archive *);\n__LA_DECL int archive_read_support_format_ar(struct archive *);\n__LA_DECL int archive_read_support_format_by_code(struct archive *, int);\n__LA_DECL int archive_read_support_format_cab(struct archive *);\n__LA_DECL int archive_read_support_format_cpio(struct archive *);\n__LA_DECL int archive_read_support_format_empty(struct archive *);\n__LA_DECL int archive_read_support_format_gnutar(struct archive *);\n__LA_DECL int archive_read_support_format_iso9660(struct archive *);\n__LA_DECL int archive_read_support_format_lha(struct archive *);\n__LA_DECL int archive_read_support_format_mtree(struct archive *);\n__LA_DECL int archive_read_support_format_rar(struct archive *);\n__LA_DECL int archive_read_support_format_rar5(struct archive *);\n__LA_DECL int archive_read_support_format_raw(struct archive *);\n__LA_DECL int archive_read_support_format_tar(struct archive *);\n__LA_DECL int archive_read_support_format_warc(struct archive *);\n__LA_DECL int archive_read_support_format_xar(struct archive *);\n/* archive_read_support_format_zip() enables both streamable and seekable\n * zip readers. */\n__LA_DECL int archive_read_support_format_zip(struct archive *);\n/* Reads Zip archives as stream from beginning to end.  Doesn't\n * correctly handle SFX ZIP files or ZIP archives that have been modified\n * in-place. */\n__LA_DECL int archive_read_support_format_zip_streamable(struct archive *);\n/* Reads starting from central directory; requires seekable input. */\n__LA_DECL int archive_read_support_format_zip_seekable(struct archive *);\n\n/* Functions to manually set the format and filters to be used. This is\n * useful to bypass the bidding process when the format and filters to use\n * is known in advance.\n */\n__LA_DECL int archive_read_set_format(struct archive *, int);\n__LA_DECL int archive_read_append_filter(struct archive *, int);\n__LA_DECL int archive_read_append_filter_program(struct archive *,\n    const char *);\n__LA_DECL int archive_read_append_filter_program_signature\n    (struct archive *, const char *, const void * /* match */, size_t);\n\n/* Set various callbacks. */\n__LA_DECL int archive_read_set_open_callback(struct archive *,\n    archive_open_callback *);\n__LA_DECL int archive_read_set_read_callback(struct archive *,\n    archive_read_callback *);\n__LA_DECL int archive_read_set_seek_callback(struct archive *,\n    archive_seek_callback *);\n__LA_DECL int archive_read_set_skip_callback(struct archive *,\n    archive_skip_callback *);\n__LA_DECL int archive_read_set_close_callback(struct archive *,\n    archive_close_callback *);\n/* Callback used to switch between one data object to the next */\n__LA_DECL int archive_read_set_switch_callback(struct archive *,\n    archive_switch_callback *);\n\n/* This sets the first data object. */\n__LA_DECL int archive_read_set_callback_data(struct archive *, void *);\n/* This sets data object at specified index */\n__LA_DECL int archive_read_set_callback_data2(struct archive *, void *,\n    unsigned int);\n/* This adds a data object at the specified index. */\n__LA_DECL int archive_read_add_callback_data(struct archive *, void *,\n    unsigned int);\n/* This appends a data object to the end of list */\n__LA_DECL int archive_read_append_callback_data(struct archive *, void *);\n/* This prepends a data object to the beginning of list */\n__LA_DECL int archive_read_prepend_callback_data(struct archive *, void *);\n\n/* Opening freezes the callbacks. */\n__LA_DECL int archive_read_open1(struct archive *);\n\n/* Convenience wrappers around the above. */\n__LA_DECL int archive_read_open(struct archive *, void *_client_data,\n\t\t     archive_open_callback *, archive_read_callback *,\n\t\t     archive_close_callback *);\n__LA_DECL int archive_read_open2(struct archive *, void *_client_data,\n\t\t     archive_open_callback *, archive_read_callback *,\n\t\t     archive_skip_callback *, archive_close_callback *);\n\n/*\n * A variety of shortcuts that invoke archive_read_open() with\n * canned callbacks suitable for common situations.  The ones that\n * accept a block size handle tape blocking correctly.\n */\n/* Use this if you know the filename.  Note: NULL indicates stdin. */\n__LA_DECL int archive_read_open_filename(struct archive *,\n\t\t     const char *_filename, size_t _block_size);\n/* Use this for reading multivolume files by filenames.\n * NOTE: Must be NULL terminated. Sorting is NOT done. */\n__LA_DECL int archive_read_open_filenames(struct archive *,\n\t\t     const char **_filenames, size_t _block_size);\n__LA_DECL int archive_read_open_filename_w(struct archive *,\n\t\t     const wchar_t *_filename, size_t _block_size);\n#if defined(_WIN32) && !defined(__CYGWIN__)\n__LA_DECL int archive_read_open_filenames_w(struct archive *,\n\t\t     const wchar_t **_filenames, size_t _block_size);\n#endif\n/* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */\n__LA_DECL int archive_read_open_file(struct archive *,\n\t\t     const char *_filename, size_t _block_size) __LA_DEPRECATED;\n/* Read an archive that's stored in memory. */\n__LA_DECL int archive_read_open_memory(struct archive *,\n\t\t     const void * buff, size_t size);\n/* A more involved version that is only used for internal testing. */\n__LA_DECL int archive_read_open_memory2(struct archive *a, const void *buff,\n\t\t     size_t size, size_t read_size);\n/* Read an archive that's already open, using the file descriptor. */\n__LA_DECL int archive_read_open_fd(struct archive *, int _fd,\n\t\t     size_t _block_size);\n/* Read an archive that's already open, using a FILE *. */\n/* Note: DO NOT use this with tape drives. */\n__LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file);\n\n/* Parses and returns next entry header. */\n__LA_DECL int archive_read_next_header(struct archive *,\n\t\t     struct archive_entry **);\n\n/* Parses and returns next entry header using the archive_entry passed in */\n__LA_DECL int archive_read_next_header2(struct archive *,\n\t\t     struct archive_entry *);\n\n/*\n * Retrieve the byte offset in UNCOMPRESSED data where last-read\n * header started.\n */\n__LA_DECL la_int64_t\t\t archive_read_header_position(struct archive *);\n\n/*\n * Returns 1 if the archive contains at least one encrypted entry.\n * If the archive format not support encryption at all\n * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned.\n * If for any other reason (e.g. not enough data read so far)\n * we cannot say whether there are encrypted entries, then\n * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned.\n * In general, this function will return values below zero when the\n * reader is uncertain or totally incapable of encryption support.\n * When this function returns 0 you can be sure that the reader\n * supports encryption detection but no encrypted entries have\n * been found yet.\n *\n * NOTE: If the metadata/header of an archive is also encrypted, you\n * cannot rely on the number of encrypted entries. That is why this\n * function does not return the number of encrypted entries but#\n * just shows that there are some.\n */\n__LA_DECL int\tarchive_read_has_encrypted_entries(struct archive *);\n\n/*\n * Returns a bitmask of capabilities that are supported by the archive format reader.\n * If the reader has no special capabilities, ARCHIVE_READ_FORMAT_CAPS_NONE is returned.\n */\n__LA_DECL int\t\t archive_read_format_capabilities(struct archive *);\n\n/* Read data from the body of an entry.  Similar to read(2). */\n__LA_DECL la_ssize_t\t\t archive_read_data(struct archive *,\n\t\t\t\t    void *, size_t);\n\n/* Seek within the body of an entry.  Similar to lseek(2). */\n__LA_DECL la_int64_t archive_seek_data(struct archive *, la_int64_t, int);\n\n/*\n * A zero-copy version of archive_read_data that also exposes the file offset\n * of each returned block.  Note that the client has no way to specify\n * the desired size of the block.  The API does guarantee that offsets will\n * be strictly increasing and that returned blocks will not overlap.\n */\n__LA_DECL int archive_read_data_block(struct archive *a,\n\t\t    const void **buff, size_t *size, la_int64_t *offset);\n\n/*-\n * Some convenience functions that are built on archive_read_data:\n *  'skip': skips entire entry\n *  'into_buffer': writes data into memory buffer that you provide\n *  'into_fd': writes data to specified filedes\n */\n__LA_DECL int archive_read_data_skip(struct archive *);\n__LA_DECL int archive_read_data_into_fd(struct archive *, int fd);\n\n/*\n * Set read options.\n */\n/* Apply option to the format only. */\n__LA_DECL int archive_read_set_format_option(struct archive *_a,\n\t\t\t    const char *m, const char *o,\n\t\t\t    const char *v);\n/* Apply option to the filter only. */\n__LA_DECL int archive_read_set_filter_option(struct archive *_a,\n\t\t\t    const char *m, const char *o,\n\t\t\t    const char *v);\n/* Apply option to both the format and the filter. */\n__LA_DECL int archive_read_set_option(struct archive *_a,\n\t\t\t    const char *m, const char *o,\n\t\t\t    const char *v);\n/* Apply option string to both the format and the filter. */\n__LA_DECL int archive_read_set_options(struct archive *_a,\n\t\t\t    const char *opts);\n\n/*\n * Add a decryption passphrase.\n */\n__LA_DECL int archive_read_add_passphrase(struct archive *, const char *);\n__LA_DECL int archive_read_set_passphrase_callback(struct archive *,\n\t\t\t    void *client_data, archive_passphrase_callback *);\n\n\n/*-\n * Convenience function to recreate the current entry (whose header\n * has just been read) on disk.\n *\n * This does quite a bit more than just copy data to disk. It also:\n *  - Creates intermediate directories as required.\n *  - Manages directory permissions:  non-writable directories will\n *    be initially created with write permission enabled; when the\n *    archive is closed, dir permissions are edited to the values specified\n *    in the archive.\n *  - Checks hardlinks:  hardlinks will not be extracted unless the\n *    linked-to file was also extracted within the same session. (TODO)\n */\n\n/* The \"flags\" argument selects optional behavior, 'OR' the flags you want. */\n\n/* Default: Do not try to set owner/group. */\n#define\tARCHIVE_EXTRACT_OWNER\t\t\t(0x0001)\n/* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */\n#define\tARCHIVE_EXTRACT_PERM\t\t\t(0x0002)\n/* Default: Do not restore mtime/atime. */\n#define\tARCHIVE_EXTRACT_TIME\t\t\t(0x0004)\n/* Default: Replace existing files. */\n#define\tARCHIVE_EXTRACT_NO_OVERWRITE \t\t(0x0008)\n/* Default: Try create first, unlink only if create fails with EEXIST. */\n#define\tARCHIVE_EXTRACT_UNLINK\t\t\t(0x0010)\n/* Default: Do not restore ACLs. */\n#define\tARCHIVE_EXTRACT_ACL\t\t\t(0x0020)\n/* Default: Do not restore fflags. */\n#define\tARCHIVE_EXTRACT_FFLAGS\t\t\t(0x0040)\n/* Default: Do not restore xattrs. */\n#define\tARCHIVE_EXTRACT_XATTR \t\t\t(0x0080)\n/* Default: Do not try to guard against extracts redirected by symlinks. */\n/* Note: With ARCHIVE_EXTRACT_UNLINK, will remove any intermediate symlink. */\n#define\tARCHIVE_EXTRACT_SECURE_SYMLINKS\t\t(0x0100)\n/* Default: Do not reject entries with '..' as path elements. */\n#define\tARCHIVE_EXTRACT_SECURE_NODOTDOT\t\t(0x0200)\n/* Default: Create parent directories as needed. */\n#define\tARCHIVE_EXTRACT_NO_AUTODIR\t\t(0x0400)\n/* Default: Overwrite files, even if one on disk is newer. */\n#define\tARCHIVE_EXTRACT_NO_OVERWRITE_NEWER\t(0x0800)\n/* Detect blocks of 0 and write holes instead. */\n#define\tARCHIVE_EXTRACT_SPARSE\t\t\t(0x1000)\n/* Default: Do not restore Mac extended metadata. */\n/* This has no effect except on Mac OS. */\n#define\tARCHIVE_EXTRACT_MAC_METADATA\t\t(0x2000)\n/* Default: Use HFS+ compression if it was compressed. */\n/* This has no effect except on Mac OS v10.6 or later. */\n#define\tARCHIVE_EXTRACT_NO_HFS_COMPRESSION\t(0x4000)\n/* Default: Do not use HFS+ compression if it was not compressed. */\n/* This has no effect except on Mac OS v10.6 or later. */\n#define\tARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED\t(0x8000)\n/* Default: Do not reject entries with absolute paths */\n#define ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS (0x10000)\n/* Default: Do not clear no-change flags when unlinking object */\n#define\tARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS\t(0x20000)\n/* Default: Do not extract atomically (using rename) */\n#define\tARCHIVE_EXTRACT_SAFE_WRITES\t\t(0x40000)\n\n__LA_DECL int archive_read_extract(struct archive *, struct archive_entry *,\n\t\t     int flags);\n__LA_DECL int archive_read_extract2(struct archive *, struct archive_entry *,\n\t\t     struct archive * /* dest */);\n__LA_DECL void\t archive_read_extract_set_progress_callback(struct archive *,\n\t\t     void (*_progress_func)(void *), void *_user_data);\n\n/* Record the dev/ino of a file that will not be written.  This is\n * generally set to the dev/ino of the archive being read. */\n__LA_DECL void\t\tarchive_read_extract_set_skip_file(struct archive *,\n\t\t     la_int64_t, la_int64_t);\n\n/* Close the file and release most resources. */\n__LA_DECL int\t\t archive_read_close(struct archive *);\n/* Release all resources and destroy the object. */\n/* Note that archive_read_free will call archive_read_close for you. */\n__LA_DECL int\t\t archive_read_free(struct archive *);\n#if ARCHIVE_VERSION_NUMBER < 4000000\n/* Synonym for archive_read_free() for backwards compatibility. */\n__LA_DECL int\t\t archive_read_finish(struct archive *) __LA_DEPRECATED;\n#endif\n\n/*-\n * To create an archive:\n *   1) Ask archive_write_new for an archive writer object.\n *   2) Set any global properties.  In particular, you should set\n *      the compression and format to use.\n *   3) Call archive_write_open to open the file (most people\n *       will use archive_write_open_file or archive_write_open_fd,\n *       which provide convenient canned I/O callbacks for you).\n *   4) For each entry:\n *      - construct an appropriate struct archive_entry structure\n *      - archive_write_header to write the header\n *      - archive_write_data to write the entry data\n *   5) archive_write_close to close the output\n *   6) archive_write_free to cleanup the writer and release resources\n */\n__LA_DECL struct archive\t*archive_write_new(void);\n__LA_DECL int archive_write_set_bytes_per_block(struct archive *,\n\t\t     int bytes_per_block);\n__LA_DECL int archive_write_get_bytes_per_block(struct archive *);\n/* XXX This is badly misnamed; suggestions appreciated. XXX */\n__LA_DECL int archive_write_set_bytes_in_last_block(struct archive *,\n\t\t     int bytes_in_last_block);\n__LA_DECL int archive_write_get_bytes_in_last_block(struct archive *);\n\n/* The dev/ino of a file that won't be archived.  This is used\n * to avoid recursively adding an archive to itself. */\n__LA_DECL int archive_write_set_skip_file(struct archive *,\n    la_int64_t, la_int64_t);\n\n#if ARCHIVE_VERSION_NUMBER < 4000000\n__LA_DECL int archive_write_set_compression_bzip2(struct archive *)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_write_set_compression_compress(struct archive *)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_write_set_compression_gzip(struct archive *)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_write_set_compression_lzip(struct archive *)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_write_set_compression_lzma(struct archive *)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_write_set_compression_none(struct archive *)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_write_set_compression_program(struct archive *,\n\t\t     const char *cmd) __LA_DEPRECATED;\n__LA_DECL int archive_write_set_compression_xz(struct archive *)\n\t\t__LA_DEPRECATED;\n#endif\n\n/* A convenience function to set the filter based on the code. */\n__LA_DECL int archive_write_add_filter(struct archive *, int filter_code);\n__LA_DECL int archive_write_add_filter_by_name(struct archive *,\n\t\t     const char *name);\n__LA_DECL int archive_write_add_filter_b64encode(struct archive *);\n__LA_DECL int archive_write_add_filter_bzip2(struct archive *);\n__LA_DECL int archive_write_add_filter_compress(struct archive *);\n__LA_DECL int archive_write_add_filter_grzip(struct archive *);\n__LA_DECL int archive_write_add_filter_gzip(struct archive *);\n__LA_DECL int archive_write_add_filter_lrzip(struct archive *);\n__LA_DECL int archive_write_add_filter_lz4(struct archive *);\n__LA_DECL int archive_write_add_filter_lzip(struct archive *);\n__LA_DECL int archive_write_add_filter_lzma(struct archive *);\n__LA_DECL int archive_write_add_filter_lzop(struct archive *);\n__LA_DECL int archive_write_add_filter_none(struct archive *);\n__LA_DECL int archive_write_add_filter_program(struct archive *,\n\t\t     const char *cmd);\n__LA_DECL int archive_write_add_filter_uuencode(struct archive *);\n__LA_DECL int archive_write_add_filter_xz(struct archive *);\n__LA_DECL int archive_write_add_filter_zstd(struct archive *);\n\n\n/* A convenience function to set the format based on the code or name. */\n__LA_DECL int archive_write_set_format(struct archive *, int format_code);\n__LA_DECL int archive_write_set_format_by_name(struct archive *,\n\t\t     const char *name);\n/* To minimize link pollution, use one or more of the following. */\n__LA_DECL int archive_write_set_format_7zip(struct archive *);\n__LA_DECL int archive_write_set_format_ar_bsd(struct archive *);\n__LA_DECL int archive_write_set_format_ar_svr4(struct archive *);\n__LA_DECL int archive_write_set_format_cpio(struct archive *);\n__LA_DECL int archive_write_set_format_cpio_bin(struct archive *);\n__LA_DECL int archive_write_set_format_cpio_newc(struct archive *);\n__LA_DECL int archive_write_set_format_cpio_odc(struct archive *);\n__LA_DECL int archive_write_set_format_cpio_pwb(struct archive *);\n__LA_DECL int archive_write_set_format_gnutar(struct archive *);\n__LA_DECL int archive_write_set_format_iso9660(struct archive *);\n__LA_DECL int archive_write_set_format_mtree(struct archive *);\n__LA_DECL int archive_write_set_format_mtree_classic(struct archive *);\n/* TODO: int archive_write_set_format_old_tar(struct archive *); */\n__LA_DECL int archive_write_set_format_pax(struct archive *);\n__LA_DECL int archive_write_set_format_pax_restricted(struct archive *);\n__LA_DECL int archive_write_set_format_raw(struct archive *);\n__LA_DECL int archive_write_set_format_shar(struct archive *);\n__LA_DECL int archive_write_set_format_shar_dump(struct archive *);\n__LA_DECL int archive_write_set_format_ustar(struct archive *);\n__LA_DECL int archive_write_set_format_v7tar(struct archive *);\n__LA_DECL int archive_write_set_format_warc(struct archive *);\n__LA_DECL int archive_write_set_format_xar(struct archive *);\n__LA_DECL int archive_write_set_format_zip(struct archive *);\n__LA_DECL int archive_write_set_format_filter_by_ext(struct archive *a, const char *filename);\n__LA_DECL int archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext);\n__LA_DECL int archive_write_zip_set_compression_deflate(struct archive *);\n__LA_DECL int archive_write_zip_set_compression_store(struct archive *);\n/* Deprecated; use archive_write_open2 instead */\n__LA_DECL int archive_write_open(struct archive *, void *,\n\t\t     archive_open_callback *, archive_write_callback *,\n\t\t     archive_close_callback *);\n__LA_DECL int archive_write_open2(struct archive *, void *,\n\t\t     archive_open_callback *, archive_write_callback *,\n\t\t     archive_close_callback *, archive_free_callback *);\n__LA_DECL int archive_write_open_fd(struct archive *, int _fd);\n__LA_DECL int archive_write_open_filename(struct archive *, const char *_file);\n__LA_DECL int archive_write_open_filename_w(struct archive *,\n\t\t     const wchar_t *_file);\n/* A deprecated synonym for archive_write_open_filename() */\n__LA_DECL int archive_write_open_file(struct archive *, const char *_file)\n\t\t__LA_DEPRECATED;\n__LA_DECL int archive_write_open_FILE(struct archive *, FILE *);\n/* _buffSize is the size of the buffer, _used refers to a variable that\n * will be updated after each write into the buffer. */\n__LA_DECL int archive_write_open_memory(struct archive *,\n\t\t\tvoid *_buffer, size_t _buffSize, size_t *_used);\n\n/*\n * Note that the library will truncate writes beyond the size provided\n * to archive_write_header or pad if the provided data is short.\n */\n__LA_DECL int archive_write_header(struct archive *,\n\t\t     struct archive_entry *);\n__LA_DECL la_ssize_t\tarchive_write_data(struct archive *,\n\t\t\t    const void *, size_t);\n\n/* This interface is currently only available for archive_write_disk handles.  */\n__LA_DECL la_ssize_t\t archive_write_data_block(struct archive *,\n\t\t\t\t    const void *, size_t, la_int64_t);\n\n__LA_DECL int\t\t archive_write_finish_entry(struct archive *);\n__LA_DECL int\t\t archive_write_close(struct archive *);\n/* Marks the archive as FATAL so that a subsequent free() operation\n * won't try to close() cleanly.  Provides a fast abort capability\n * when the client discovers that things have gone wrong. */\n__LA_DECL int            archive_write_fail(struct archive *);\n/* This can fail if the archive wasn't already closed, in which case\n * archive_write_free() will implicitly call archive_write_close(). */\n__LA_DECL int\t\t archive_write_free(struct archive *);\n#if ARCHIVE_VERSION_NUMBER < 4000000\n/* Synonym for archive_write_free() for backwards compatibility. */\n__LA_DECL int\t\t archive_write_finish(struct archive *) __LA_DEPRECATED;\n#endif\n\n/*\n * Set write options.\n */\n/* Apply option to the format only. */\n__LA_DECL int archive_write_set_format_option(struct archive *_a,\n\t\t\t    const char *m, const char *o,\n\t\t\t    const char *v);\n/* Apply option to the filter only. */\n__LA_DECL int archive_write_set_filter_option(struct archive *_a,\n\t\t\t    const char *m, const char *o,\n\t\t\t    const char *v);\n/* Apply option to both the format and the filter. */\n__LA_DECL int archive_write_set_option(struct archive *_a,\n\t\t\t    const char *m, const char *o,\n\t\t\t    const char *v);\n/* Apply option string to both the format and the filter. */\n__LA_DECL int archive_write_set_options(struct archive *_a,\n\t\t\t    const char *opts);\n\n/*\n * Set an encryption passphrase.\n */\n__LA_DECL int archive_write_set_passphrase(struct archive *_a, const char *p);\n__LA_DECL int archive_write_set_passphrase_callback(struct archive *,\n\t\t\t    void *client_data, archive_passphrase_callback *);\n\n/*-\n * ARCHIVE_WRITE_DISK API\n *\n * To create objects on disk:\n *   1) Ask archive_write_disk_new for a new archive_write_disk object.\n *   2) Set any global properties.  In particular, you probably\n *      want to set the options.\n *   3) For each entry:\n *      - construct an appropriate struct archive_entry structure\n *      - archive_write_header to create the file/dir/etc on disk\n *      - archive_write_data to write the entry data\n *   4) archive_write_free to cleanup the writer and release resources\n *\n * In particular, you can use this in conjunction with archive_read()\n * to pull entries out of an archive and create them on disk.\n */\n__LA_DECL struct archive\t*archive_write_disk_new(void);\n/* This file will not be overwritten. */\n__LA_DECL int archive_write_disk_set_skip_file(struct archive *,\n    la_int64_t, la_int64_t);\n/* Set flags to control how the next item gets created.\n * This accepts a bitmask of ARCHIVE_EXTRACT_XXX flags defined above. */\n__LA_DECL int\t\t archive_write_disk_set_options(struct archive *,\n\t\t     int flags);\n/*\n * The lookup functions are given uname/uid (or gname/gid) pairs and\n * return a uid (gid) suitable for this system.  These are used for\n * restoring ownership and for setting ACLs.  The default functions\n * are naive, they just return the uid/gid.  These are small, so reasonable\n * for applications that don't need to preserve ownership; they\n * are probably also appropriate for applications that are doing\n * same-system backup and restore.\n */\n/*\n * The \"standard\" lookup functions use common system calls to lookup\n * the uname/gname, falling back to the uid/gid if the names can't be\n * found.  They cache lookups and are reasonably fast, but can be very\n * large, so they are not used unless you ask for them.  In\n * particular, these match the specifications of POSIX \"pax\" and old\n * POSIX \"tar\".\n */\n__LA_DECL int\t archive_write_disk_set_standard_lookup(struct archive *);\n/*\n * If neither the default (naive) nor the standard (big) functions suit\n * your needs, you can write your own and register them.  Be sure to\n * include a cleanup function if you have allocated private data.\n */\n__LA_DECL int archive_write_disk_set_group_lookup(struct archive *,\n    void * /* private_data */,\n    la_int64_t (*)(void *, const char *, la_int64_t),\n    void (* /* cleanup */)(void *));\n__LA_DECL int archive_write_disk_set_user_lookup(struct archive *,\n    void * /* private_data */,\n    la_int64_t (*)(void *, const char *, la_int64_t),\n    void (* /* cleanup */)(void *));\n__LA_DECL la_int64_t archive_write_disk_gid(struct archive *, const char *, la_int64_t);\n__LA_DECL la_int64_t archive_write_disk_uid(struct archive *, const char *, la_int64_t);\n\n/*\n * ARCHIVE_READ_DISK API\n *\n * This is still evolving and somewhat experimental.\n */\n__LA_DECL struct archive *archive_read_disk_new(void);\n/* The names for symlink modes here correspond to an old BSD\n * command-line argument convention: -L, -P, -H */\n/* Follow all symlinks. */\n__LA_DECL int archive_read_disk_set_symlink_logical(struct archive *);\n/* Follow no symlinks. */\n__LA_DECL int archive_read_disk_set_symlink_physical(struct archive *);\n/* Follow symlink initially, then not. */\n__LA_DECL int archive_read_disk_set_symlink_hybrid(struct archive *);\n/* TODO: Handle Linux stat32/stat64 ugliness. <sigh> */\n__LA_DECL int archive_read_disk_entry_from_file(struct archive *,\n    struct archive_entry *, int /* fd */, const struct stat *);\n/* Look up gname for gid or uname for uid. */\n/* Default implementations are very, very stupid. */\n__LA_DECL const char *archive_read_disk_gname(struct archive *, la_int64_t);\n__LA_DECL const char *archive_read_disk_uname(struct archive *, la_int64_t);\n/* \"Standard\" implementation uses getpwuid_r, getgrgid_r and caches the\n * results for performance. */\n__LA_DECL int\tarchive_read_disk_set_standard_lookup(struct archive *);\n/* You can install your own lookups if you like. */\n__LA_DECL int\tarchive_read_disk_set_gname_lookup(struct archive *,\n    void * /* private_data */,\n    const char *(* /* lookup_fn */)(void *, la_int64_t),\n    void (* /* cleanup_fn */)(void *));\n__LA_DECL int\tarchive_read_disk_set_uname_lookup(struct archive *,\n    void * /* private_data */,\n    const char *(* /* lookup_fn */)(void *, la_int64_t),\n    void (* /* cleanup_fn */)(void *));\n/* Start traversal. */\n__LA_DECL int\tarchive_read_disk_open(struct archive *, const char *);\n__LA_DECL int\tarchive_read_disk_open_w(struct archive *, const wchar_t *);\n/*\n * Request that current entry be visited.  If you invoke it on every\n * directory, you'll get a physical traversal.  This is ignored if the\n * current entry isn't a directory or a link to a directory.  So, if\n * you invoke this on every returned path, you'll get a full logical\n * traversal.\n */\n__LA_DECL int\tarchive_read_disk_descend(struct archive *);\n__LA_DECL int\tarchive_read_disk_can_descend(struct archive *);\n__LA_DECL int\tarchive_read_disk_current_filesystem(struct archive *);\n__LA_DECL int\tarchive_read_disk_current_filesystem_is_synthetic(struct archive *);\n__LA_DECL int\tarchive_read_disk_current_filesystem_is_remote(struct archive *);\n/* Request that the access time of the entry visited by traversal be restored. */\n__LA_DECL int  archive_read_disk_set_atime_restored(struct archive *);\n/*\n * Set behavior. The \"flags\" argument selects optional behavior.\n */\n/* Request that the access time of the entry visited by traversal be restored.\n * This is the same as archive_read_disk_set_atime_restored. */\n#define\tARCHIVE_READDISK_RESTORE_ATIME\t\t(0x0001)\n/* Default: Do not skip an entry which has nodump flags. */\n#define\tARCHIVE_READDISK_HONOR_NODUMP\t\t(0x0002)\n/* Default: Skip a mac resource fork file whose prefix is \"._\" because of\n * using copyfile. */\n#define\tARCHIVE_READDISK_MAC_COPYFILE\t\t(0x0004)\n/* Default: Traverse mount points. */\n#define\tARCHIVE_READDISK_NO_TRAVERSE_MOUNTS\t(0x0008)\n/* Default: Xattrs are read from disk. */\n#define\tARCHIVE_READDISK_NO_XATTR\t\t(0x0010)\n/* Default: ACLs are read from disk. */\n#define\tARCHIVE_READDISK_NO_ACL\t\t\t(0x0020)\n/* Default: File flags are read from disk. */\n#define\tARCHIVE_READDISK_NO_FFLAGS\t\t(0x0040)\n/* Default: Sparse file information is read from disk. */\n#define\tARCHIVE_READDISK_NO_SPARSE\t\t(0x0080)\n\n__LA_DECL int  archive_read_disk_set_behavior(struct archive *,\n\t\t    int flags);\n\n/*\n * Set archive_match object that will be used in archive_read_disk to\n * know whether an entry should be skipped. The callback function\n * _excluded_func will be invoked when an entry is skipped by the result\n * of archive_match.\n */\n__LA_DECL int\tarchive_read_disk_set_matching(struct archive *,\n\t\t    struct archive *_matching, void (*_excluded_func)\n\t\t    (struct archive *, void *, struct archive_entry *),\n\t\t    void *_client_data);\n__LA_DECL int\tarchive_read_disk_set_metadata_filter_callback(struct archive *,\n\t\t    int (*_metadata_filter_func)(struct archive *, void *,\n\t\t    \tstruct archive_entry *), void *_client_data);\n\n/* Simplified cleanup interface;\n * This calls archive_read_free() or archive_write_free() as needed. */\n__LA_DECL int\tarchive_free(struct archive *);\n\n/*\n * Accessor functions to read/set various information in\n * the struct archive object:\n */\n\n/* Number of filters in the current filter pipeline. */\n/* Filter #0 is the one closest to the format, -1 is a synonym for the\n * last filter, which is always the pseudo-filter that wraps the\n * client callbacks. */\n__LA_DECL int\t\t archive_filter_count(struct archive *);\n__LA_DECL la_int64_t\t archive_filter_bytes(struct archive *, int);\n__LA_DECL int\t\t archive_filter_code(struct archive *, int);\n__LA_DECL const char *\t archive_filter_name(struct archive *, int);\n\n#if ARCHIVE_VERSION_NUMBER < 4000000\n/* These don't properly handle multiple filters, so are deprecated and\n * will eventually be removed. */\n/* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, -1); */\n__LA_DECL la_int64_t\t archive_position_compressed(struct archive *)\n\t\t\t\t__LA_DEPRECATED;\n/* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, 0); */\n__LA_DECL la_int64_t\t archive_position_uncompressed(struct archive *)\n\t\t\t\t__LA_DEPRECATED;\n/* As of libarchive 3.0, this is an alias for archive_filter_name(a, 0); */\n__LA_DECL const char\t*archive_compression_name(struct archive *)\n\t\t\t\t__LA_DEPRECATED;\n/* As of libarchive 3.0, this is an alias for archive_filter_code(a, 0); */\n__LA_DECL int\t\t archive_compression(struct archive *)\n\t\t\t\t__LA_DEPRECATED;\n#endif\n\n__LA_DECL int\t\t archive_errno(struct archive *);\n__LA_DECL const char\t*archive_error_string(struct archive *);\n__LA_DECL const char\t*archive_format_name(struct archive *);\n__LA_DECL int\t\t archive_format(struct archive *);\n__LA_DECL void\t\t archive_clear_error(struct archive *);\n__LA_DECL void\t\t archive_set_error(struct archive *, int _err,\n\t\t\t    const char *fmt, ...) __LA_PRINTF(3, 4);\n__LA_DECL void\t\t archive_copy_error(struct archive *dest,\n\t\t\t    struct archive *src);\n__LA_DECL int\t\t archive_file_count(struct archive *);\n\n/*\n * ARCHIVE_MATCH API\n */\n__LA_DECL struct archive *archive_match_new(void);\n__LA_DECL int\tarchive_match_free(struct archive *);\n\n/*\n * Test if archive_entry is excluded.\n * This is a convenience function. This is the same as calling all\n * archive_match_path_excluded, archive_match_time_excluded\n * and archive_match_owner_excluded.\n */\n__LA_DECL int\tarchive_match_excluded(struct archive *,\n\t\t    struct archive_entry *);\n\n/*\n * Test if pathname is excluded. The conditions are set by following functions.\n */\n__LA_DECL int\tarchive_match_path_excluded(struct archive *,\n\t\t    struct archive_entry *);\n/* Control recursive inclusion of directory content when directory is included. Default on. */\n__LA_DECL int\tarchive_match_set_inclusion_recursion(struct archive *, int);\n/* Add exclusion pathname pattern. */\n__LA_DECL int\tarchive_match_exclude_pattern(struct archive *, const char *);\n__LA_DECL int\tarchive_match_exclude_pattern_w(struct archive *,\n\t\t    const wchar_t *);\n/* Add exclusion pathname pattern from file. */\n__LA_DECL int\tarchive_match_exclude_pattern_from_file(struct archive *,\n\t\t    const char *, int _nullSeparator);\n__LA_DECL int\tarchive_match_exclude_pattern_from_file_w(struct archive *,\n\t\t    const wchar_t *, int _nullSeparator);\n/* Add inclusion pathname pattern. */\n__LA_DECL int\tarchive_match_include_pattern(struct archive *, const char *);\n__LA_DECL int\tarchive_match_include_pattern_w(struct archive *,\n\t\t    const wchar_t *);\n/* Add inclusion pathname pattern from file. */\n__LA_DECL int\tarchive_match_include_pattern_from_file(struct archive *,\n\t\t    const char *, int _nullSeparator);\n__LA_DECL int\tarchive_match_include_pattern_from_file_w(struct archive *,\n\t\t    const wchar_t *, int _nullSeparator);\n/*\n * How to get statistic information for inclusion patterns.\n */\n/* Return the amount number of unmatched inclusion patterns. */\n__LA_DECL int\tarchive_match_path_unmatched_inclusions(struct archive *);\n/* Return the pattern of unmatched inclusion with ARCHIVE_OK.\n * Return ARCHIVE_EOF if there is no inclusion pattern. */\n__LA_DECL int\tarchive_match_path_unmatched_inclusions_next(\n\t\t    struct archive *, const char **);\n__LA_DECL int\tarchive_match_path_unmatched_inclusions_next_w(\n\t\t    struct archive *, const wchar_t **);\n\n/*\n * Test if a file is excluded by its time stamp.\n * The conditions are set by following functions.\n */\n__LA_DECL int\tarchive_match_time_excluded(struct archive *,\n\t\t    struct archive_entry *);\n\n/*\n * Flags to tell a matching type of time stamps. These are used for\n * following functions.\n */\n/* Time flag: mtime to be tested. */\n#define ARCHIVE_MATCH_MTIME\t(0x0100)\n/* Time flag: ctime to be tested. */\n#define ARCHIVE_MATCH_CTIME\t(0x0200)\n/* Comparison flag: Match the time if it is newer than. */\n#define ARCHIVE_MATCH_NEWER\t(0x0001)\n/* Comparison flag: Match the time if it is older than. */\n#define ARCHIVE_MATCH_OLDER\t(0x0002)\n/* Comparison flag: Match the time if it is equal to. */\n#define ARCHIVE_MATCH_EQUAL\t(0x0010)\n/* Set inclusion time. */\n__LA_DECL int\tarchive_match_include_time(struct archive *, int _flag,\n\t\t    time_t _sec, long _nsec);\n/* Set inclusion time by a date string. */\n__LA_DECL int\tarchive_match_include_date(struct archive *, int _flag,\n\t\t    const char *_datestr);\n__LA_DECL int\tarchive_match_include_date_w(struct archive *, int _flag,\n\t\t    const wchar_t *_datestr);\n/* Set inclusion time by a particular file. */\n__LA_DECL int\tarchive_match_include_file_time(struct archive *,\n\t\t    int _flag, const char *_pathname);\n__LA_DECL int\tarchive_match_include_file_time_w(struct archive *,\n\t\t    int _flag, const wchar_t *_pathname);\n/* Add exclusion entry. */\n__LA_DECL int\tarchive_match_exclude_entry(struct archive *,\n\t\t    int _flag, struct archive_entry *);\n\n/*\n * Test if a file is excluded by its uid ,gid, uname or gname.\n * The conditions are set by following functions.\n */\n__LA_DECL int\tarchive_match_owner_excluded(struct archive *,\n\t\t    struct archive_entry *);\n/* Add inclusion uid, gid, uname and gname. */\n__LA_DECL int\tarchive_match_include_uid(struct archive *, la_int64_t);\n__LA_DECL int\tarchive_match_include_gid(struct archive *, la_int64_t);\n__LA_DECL int\tarchive_match_include_uname(struct archive *, const char *);\n__LA_DECL int\tarchive_match_include_uname_w(struct archive *,\n\t\t    const wchar_t *);\n__LA_DECL int\tarchive_match_include_gname(struct archive *, const char *);\n__LA_DECL int\tarchive_match_include_gname_w(struct archive *,\n\t\t    const wchar_t *);\n\n/* Utility functions */\n/* Convenience function to sort a NULL terminated list of strings */\n__LA_DECL int archive_utility_string_sort(char **);\n\n#ifdef __cplusplus\n}\n#endif\n\n/* These are meaningless outside of this header. */\n#undef __LA_DECL\n\n#endif /* !ARCHIVE_H_INCLUDED */\n"
  },
  {
    "path": "Sources/ContainerizationArchive/CArchive/include/archive_bridge.h",
    "content": "//\n\n#pragma once\n\n#include \"archive.h\"\n#include <stdint.h>\n\nvoid archive_set_error_wrapper(struct archive *a, int error_number, const char *error_string);\n\n/// Decompress a zstd-compressed file at \\p src_fd into \\p dst_fd.\n/// Returns 0 on success, or a non-zero error code on failure.\nint zstd_decompress_fd(int src_fd, int dst_fd);\n"
  },
  {
    "path": "Sources/ContainerizationArchive/CArchive/include/archive_entry.h",
    "content": "/*-\n * Copyright (c) 2003-2008 Tim Kientzle\n * Copyright (c) 2016 Martin Matuska\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR\n * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef ARCHIVE_ENTRY_H_INCLUDED\n#define\tARCHIVE_ENTRY_H_INCLUDED\n\n/* Note: Compiler will complain if this does not match archive.h! */\n#define\tARCHIVE_VERSION_NUMBER 3007007\n\n/*\n * Note: archive_entry.h is for use outside of libarchive; the\n * configuration headers (config.h, archive_platform.h, etc.) are\n * purely internal.  Do NOT use HAVE_XXX configuration macros to\n * control the behavior of this header!  If you must conditionalize,\n * use predefined compiler and/or platform macros.\n */\n\n#include <sys/types.h>\n#include <stddef.h>  /* for wchar_t */\n#include <stdint.h>\n#include <time.h>\n\n#if defined(_WIN32) && !defined(__CYGWIN__)\n#include <windows.h>\n#endif\n\n/* Get a suitable 64-bit integer type. */\n#if !defined(__LA_INT64_T_DEFINED)\n# if ARCHIVE_VERSION_NUMBER < 4000000\n#define __LA_INT64_T la_int64_t\n# endif\n#define __LA_INT64_T_DEFINED\n# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)\ntypedef __int64 la_int64_t;\n# else\n#include <unistd.h>\n#  if defined(_SCO_DS) || defined(__osf__)\ntypedef long long la_int64_t;\n#  else\ntypedef int64_t la_int64_t;\n#  endif\n# endif\n#endif\n\n/* The la_ssize_t should match the type used in 'struct stat' */\n#if !defined(__LA_SSIZE_T_DEFINED)\n/* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */\n# if ARCHIVE_VERSION_NUMBER < 4000000\n#define __LA_SSIZE_T la_ssize_t\n# endif\n#define __LA_SSIZE_T_DEFINED\n# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)\n#  if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)\ntypedef ssize_t la_ssize_t;\n#  elif defined(_WIN64)\ntypedef __int64 la_ssize_t;\n#  else\ntypedef long la_ssize_t;\n#  endif\n# else\n# include <unistd.h>  /* ssize_t */\ntypedef ssize_t la_ssize_t;\n# endif\n#endif\n\n/* Get a suitable definition for mode_t */\n#if ARCHIVE_VERSION_NUMBER >= 3999000\n/* Switch to plain 'int' for libarchive 4.0.  It's less broken than 'mode_t' */\n# define\t__LA_MODE_T\tint\n#elif defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) && !defined(__WATCOMC__)\n# define\t__LA_MODE_T\tunsigned short\n#else\n# define\t__LA_MODE_T\tmode_t\n#endif\n\n/* Large file support for Android */\n#if defined(__LIBARCHIVE_BUILD) && defined(__ANDROID__)\n#include \"android_lf.h\"\n#endif\n\n/*\n * On Windows, define LIBARCHIVE_STATIC if you're building or using a\n * .lib.  The default here assumes you're building a DLL.  Only\n * libarchive source should ever define __LIBARCHIVE_BUILD.\n */\n#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC)\n# ifdef __LIBARCHIVE_BUILD\n#  ifdef __GNUC__\n#   define __LA_DECL\t__attribute__((dllexport)) extern\n#  else\n#   define __LA_DECL\t__declspec(dllexport)\n#  endif\n# else\n#  ifdef __GNUC__\n#   define __LA_DECL\n#  else\n#   define __LA_DECL\t__declspec(dllimport)\n#  endif\n# endif\n#elif defined __LIBARCHIVE_ENABLE_VISIBILITY\n#  define __LA_DECL __attribute__((visibility(\"default\")))\n#else\n/* Static libraries on all platforms and shared libraries on non-Windows. */\n# define __LA_DECL\n#endif\n\n#if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1\n# define __LA_DEPRECATED __attribute__((deprecated))\n#else\n# define __LA_DEPRECATED\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Description of an archive entry.\n *\n * You can think of this as \"struct stat\" with some text fields added in.\n *\n * TODO: Add \"comment\", \"charset\", and possibly other entries that are\n * supported by \"pax interchange\" format.  However, GNU, ustar, cpio,\n * and other variants don't support these features, so they're not an\n * excruciatingly high priority right now.\n *\n * TODO: \"pax interchange\" format allows essentially arbitrary\n * key/value attributes to be attached to any entry.  Supporting\n * such extensions may make this library useful for special\n * applications (e.g., a package manager could attach special\n * package-management attributes to each entry).\n */\nstruct archive;\nstruct archive_entry;\n\n/*\n * File-type constants.  These are returned from archive_entry_filetype()\n * and passed to archive_entry_set_filetype().\n *\n * These values match S_XXX defines on every platform I've checked,\n * including Windows, AIX, Linux, Solaris, and BSD.  They're\n * (re)defined here because platforms generally don't define the ones\n * they don't support.  For example, Windows doesn't define S_IFLNK or\n * S_IFBLK.  Instead of having a mass of conditional logic and system\n * checks to define any S_XXX values that aren't supported locally,\n * I've just defined a new set of such constants so that\n * libarchive-based applications can manipulate and identify archive\n * entries properly even if the hosting platform can't store them on\n * disk.\n *\n * These values are also used directly within some portable formats,\n * such as cpio.  If you find a platform that varies from these, the\n * correct solution is to leave these alone and translate from these\n * portable values to platform-native values when entries are read from\n * or written to disk.\n */\n/*\n * In libarchive 4.0, we can drop the casts here.\n * They're needed to work around Borland C's broken mode_t.\n */\n#define AE_IFMT\t\t((__LA_MODE_T)0170000)\n#define AE_IFREG\t((__LA_MODE_T)0100000)\n#define AE_IFLNK\t((__LA_MODE_T)0120000)\n#define AE_IFSOCK\t((__LA_MODE_T)0140000)\n#define AE_IFCHR\t((__LA_MODE_T)0020000)\n#define AE_IFBLK\t((__LA_MODE_T)0060000)\n#define AE_IFDIR\t((__LA_MODE_T)0040000)\n#define AE_IFIFO\t((__LA_MODE_T)0010000)\n\n/*\n * Symlink types\n */\n#define AE_SYMLINK_TYPE_UNDEFINED\t0\n#define AE_SYMLINK_TYPE_FILE\t\t1\n#define AE_SYMLINK_TYPE_DIRECTORY\t2\n\n/*\n * Basic object manipulation\n */\n\n__LA_DECL struct archive_entry\t*archive_entry_clear(struct archive_entry *);\n/* The 'clone' function does a deep copy; all of the strings are copied too. */\n__LA_DECL struct archive_entry\t*archive_entry_clone(struct archive_entry *);\n__LA_DECL void\t\t\t archive_entry_free(struct archive_entry *);\n__LA_DECL struct archive_entry\t*archive_entry_new(void);\n\n/*\n * This form of archive_entry_new2() will pull character-set\n * conversion information from the specified archive handle.  The\n * older archive_entry_new(void) form is equivalent to calling\n * archive_entry_new2(NULL) and will result in the use of an internal\n * default character-set conversion.\n */\n__LA_DECL struct archive_entry\t*archive_entry_new2(struct archive *);\n\n/*\n * Retrieve fields from an archive_entry.\n *\n * There are a number of implicit conversions among these fields.  For\n * example, if a regular string field is set and you read the _w wide\n * character field, the entry will implicitly convert narrow-to-wide\n * using the current locale.  Similarly, dev values are automatically\n * updated when you write devmajor or devminor and vice versa.\n *\n * In addition, fields can be \"set\" or \"unset.\"  Unset string fields\n * return NULL, non-string fields have _is_set() functions to test\n * whether they've been set.  You can \"unset\" a string field by\n * assigning NULL; non-string fields have _unset() functions to\n * unset them.\n *\n * Note: There is one ambiguity in the above; string fields will\n * also return NULL when implicit character set conversions fail.\n * This is usually what you want.\n */\n__LA_DECL time_t\t archive_entry_atime(struct archive_entry *);\n__LA_DECL long\t\t archive_entry_atime_nsec(struct archive_entry *);\n__LA_DECL int\t\t archive_entry_atime_is_set(struct archive_entry *);\n__LA_DECL time_t\t archive_entry_birthtime(struct archive_entry *);\n__LA_DECL long\t\t archive_entry_birthtime_nsec(struct archive_entry *);\n__LA_DECL int\t\t archive_entry_birthtime_is_set(struct archive_entry *);\n__LA_DECL time_t\t archive_entry_ctime(struct archive_entry *);\n__LA_DECL long\t\t archive_entry_ctime_nsec(struct archive_entry *);\n__LA_DECL int\t\t archive_entry_ctime_is_set(struct archive_entry *);\n__LA_DECL dev_t\t\t archive_entry_dev(struct archive_entry *);\n__LA_DECL int\t\t archive_entry_dev_is_set(struct archive_entry *);\n__LA_DECL dev_t\t\t archive_entry_devmajor(struct archive_entry *);\n__LA_DECL dev_t\t\t archive_entry_devminor(struct archive_entry *);\n__LA_DECL __LA_MODE_T\t archive_entry_filetype(struct archive_entry *);\n__LA_DECL int\t\t archive_entry_filetype_is_set(struct archive_entry *);\n__LA_DECL void\t\t archive_entry_fflags(struct archive_entry *,\n\t\t\t    unsigned long * /* set */,\n\t\t\t    unsigned long * /* clear */);\n__LA_DECL const char\t*archive_entry_fflags_text(struct archive_entry *);\n__LA_DECL la_int64_t\t archive_entry_gid(struct archive_entry *);\n__LA_DECL int\t\t archive_entry_gid_is_set(struct archive_entry *);\n__LA_DECL const char\t*archive_entry_gname(struct archive_entry *);\n__LA_DECL const char\t*archive_entry_gname_utf8(struct archive_entry *);\n__LA_DECL const wchar_t\t*archive_entry_gname_w(struct archive_entry *);\n__LA_DECL void\t\t archive_entry_set_link_to_hardlink(struct archive_entry *);\n__LA_DECL const char\t*archive_entry_hardlink(struct archive_entry *);\n__LA_DECL const char\t*archive_entry_hardlink_utf8(struct archive_entry *);\n__LA_DECL const wchar_t\t*archive_entry_hardlink_w(struct archive_entry *);\n__LA_DECL int\t\t archive_entry_hardlink_is_set(struct archive_entry *);\n__LA_DECL la_int64_t\t archive_entry_ino(struct archive_entry *);\n__LA_DECL la_int64_t\t archive_entry_ino64(struct archive_entry *);\n__LA_DECL int\t\t archive_entry_ino_is_set(struct archive_entry *);\n__LA_DECL __LA_MODE_T\t archive_entry_mode(struct archive_entry *);\n__LA_DECL time_t\t archive_entry_mtime(struct archive_entry *);\n__LA_DECL long\t\t archive_entry_mtime_nsec(struct archive_entry *);\n__LA_DECL int\t\t archive_entry_mtime_is_set(struct archive_entry *);\n__LA_DECL unsigned int\t archive_entry_nlink(struct archive_entry *);\n__LA_DECL const char\t*archive_entry_pathname(struct archive_entry *);\n__LA_DECL const char\t*archive_entry_pathname_utf8(struct archive_entry *);\n__LA_DECL const wchar_t\t*archive_entry_pathname_w(struct archive_entry *);\n__LA_DECL __LA_MODE_T\t archive_entry_perm(struct archive_entry *);\n__LA_DECL int\t\t archive_entry_perm_is_set(struct archive_entry *);\n__LA_DECL int\t\t archive_entry_rdev_is_set(struct archive_entry *);\n__LA_DECL dev_t\t\t archive_entry_rdev(struct archive_entry *);\n__LA_DECL dev_t\t\t archive_entry_rdevmajor(struct archive_entry *);\n__LA_DECL dev_t\t\t archive_entry_rdevminor(struct archive_entry *);\n__LA_DECL const char\t*archive_entry_sourcepath(struct archive_entry *);\n__LA_DECL const wchar_t\t*archive_entry_sourcepath_w(struct archive_entry *);\n__LA_DECL la_int64_t\t archive_entry_size(struct archive_entry *);\n__LA_DECL int\t\t archive_entry_size_is_set(struct archive_entry *);\n__LA_DECL const char\t*archive_entry_strmode(struct archive_entry *);\n__LA_DECL void\t\t archive_entry_set_link_to_symlink(struct archive_entry *);\n__LA_DECL const char\t*archive_entry_symlink(struct archive_entry *);\n__LA_DECL const char\t*archive_entry_symlink_utf8(struct archive_entry *);\n__LA_DECL int\t\t archive_entry_symlink_type(struct archive_entry *);\n__LA_DECL const wchar_t\t*archive_entry_symlink_w(struct archive_entry *);\n__LA_DECL la_int64_t\t archive_entry_uid(struct archive_entry *);\n__LA_DECL int\t\t archive_entry_uid_is_set(struct archive_entry *);\n__LA_DECL const char\t*archive_entry_uname(struct archive_entry *);\n__LA_DECL const char\t*archive_entry_uname_utf8(struct archive_entry *);\n__LA_DECL const wchar_t\t*archive_entry_uname_w(struct archive_entry *);\n__LA_DECL int archive_entry_is_data_encrypted(struct archive_entry *);\n__LA_DECL int archive_entry_is_metadata_encrypted(struct archive_entry *);\n__LA_DECL int archive_entry_is_encrypted(struct archive_entry *);\n\n/*\n * Set fields in an archive_entry.\n *\n * Note: Before libarchive 2.4, there were 'set' and 'copy' versions\n * of the string setters.  'copy' copied the actual string, 'set' just\n * stored the pointer.  In libarchive 2.4 and later, strings are\n * always copied.\n */\n\n__LA_DECL void\tarchive_entry_set_atime(struct archive_entry *, time_t, long);\n__LA_DECL void  archive_entry_unset_atime(struct archive_entry *);\n#if defined(_WIN32) && !defined(__CYGWIN__)\n__LA_DECL void archive_entry_copy_bhfi(struct archive_entry *, BY_HANDLE_FILE_INFORMATION *);\n#endif\n__LA_DECL void\tarchive_entry_set_birthtime(struct archive_entry *, time_t, long);\n__LA_DECL void  archive_entry_unset_birthtime(struct archive_entry *);\n__LA_DECL void\tarchive_entry_set_ctime(struct archive_entry *, time_t, long);\n__LA_DECL void  archive_entry_unset_ctime(struct archive_entry *);\n__LA_DECL void\tarchive_entry_set_dev(struct archive_entry *, dev_t);\n__LA_DECL void\tarchive_entry_set_devmajor(struct archive_entry *, dev_t);\n__LA_DECL void\tarchive_entry_set_devminor(struct archive_entry *, dev_t);\n__LA_DECL void\tarchive_entry_set_filetype(struct archive_entry *, unsigned int);\n__LA_DECL void\tarchive_entry_set_fflags(struct archive_entry *,\n\t    unsigned long /* set */, unsigned long /* clear */);\n/* Returns pointer to start of first invalid token, or NULL if none. */\n/* Note that all recognized tokens are processed, regardless. */\n__LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *,\n\t    const char *);\n__LA_DECL const char *archive_entry_copy_fflags_text_len(struct archive_entry *,\n\t    const char *, size_t);\n__LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,\n\t    const wchar_t *);\n__LA_DECL void\tarchive_entry_set_gid(struct archive_entry *, la_int64_t);\n__LA_DECL void\tarchive_entry_set_gname(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_set_gname_utf8(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_copy_gname(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);\n__LA_DECL int\tarchive_entry_update_gname_utf8(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_set_hardlink(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_set_hardlink_utf8(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_copy_hardlink(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);\n__LA_DECL int\tarchive_entry_update_hardlink_utf8(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_set_ino(struct archive_entry *, la_int64_t);\n__LA_DECL void\tarchive_entry_set_ino64(struct archive_entry *, la_int64_t);\n__LA_DECL void\tarchive_entry_set_link(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_set_link_utf8(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_copy_link(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_copy_link_w(struct archive_entry *, const wchar_t *);\n__LA_DECL int\tarchive_entry_update_link_utf8(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_set_mode(struct archive_entry *, __LA_MODE_T);\n__LA_DECL void\tarchive_entry_set_mtime(struct archive_entry *, time_t, long);\n__LA_DECL void  archive_entry_unset_mtime(struct archive_entry *);\n__LA_DECL void\tarchive_entry_set_nlink(struct archive_entry *, unsigned int);\n__LA_DECL void\tarchive_entry_set_pathname(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_set_pathname_utf8(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_copy_pathname(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);\n__LA_DECL int\tarchive_entry_update_pathname_utf8(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_set_perm(struct archive_entry *, __LA_MODE_T);\n__LA_DECL void\tarchive_entry_set_rdev(struct archive_entry *, dev_t);\n__LA_DECL void\tarchive_entry_set_rdevmajor(struct archive_entry *, dev_t);\n__LA_DECL void\tarchive_entry_set_rdevminor(struct archive_entry *, dev_t);\n__LA_DECL void\tarchive_entry_set_size(struct archive_entry *, la_int64_t);\n__LA_DECL void\tarchive_entry_unset_size(struct archive_entry *);\n__LA_DECL void\tarchive_entry_copy_sourcepath(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_copy_sourcepath_w(struct archive_entry *, const wchar_t *);\n__LA_DECL void\tarchive_entry_set_symlink(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_set_symlink_type(struct archive_entry *, int);\n__LA_DECL void\tarchive_entry_set_symlink_utf8(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_copy_symlink(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);\n__LA_DECL int\tarchive_entry_update_symlink_utf8(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_set_uid(struct archive_entry *, la_int64_t);\n__LA_DECL void\tarchive_entry_set_uname(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_set_uname_utf8(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_copy_uname(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);\n__LA_DECL int\tarchive_entry_update_uname_utf8(struct archive_entry *, const char *);\n__LA_DECL void\tarchive_entry_set_is_data_encrypted(struct archive_entry *, char is_encrypted);\n__LA_DECL void\tarchive_entry_set_is_metadata_encrypted(struct archive_entry *, char is_encrypted);\n/*\n * Routines to bulk copy fields to/from a platform-native \"struct\n * stat.\"  Libarchive used to just store a struct stat inside of each\n * archive_entry object, but this created issues when trying to\n * manipulate archives on systems different than the ones they were\n * created on.\n *\n * TODO: On Linux and other LFS systems, provide both stat32 and\n * stat64 versions of these functions and all of the macro glue so\n * that archive_entry_stat is magically defined to\n * archive_entry_stat32 or archive_entry_stat64 as appropriate.\n */\n__LA_DECL const struct stat\t*archive_entry_stat(struct archive_entry *);\n__LA_DECL void\tarchive_entry_copy_stat(struct archive_entry *, const struct stat *);\n\n/*\n * Storage for Mac OS-specific AppleDouble metadata information.\n * Apple-format tar files store a separate binary blob containing\n * encoded metadata with ACL, extended attributes, etc.\n * This provides a place to store that blob.\n */\n\n__LA_DECL const void * archive_entry_mac_metadata(struct archive_entry *, size_t *);\n__LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const void *, size_t);\n\n/*\n * Digest routine. This is used to query the raw hex digest for the\n * given entry. The type of digest is provided as an argument.\n */\n#define ARCHIVE_ENTRY_DIGEST_MD5              0x00000001\n#define ARCHIVE_ENTRY_DIGEST_RMD160           0x00000002\n#define ARCHIVE_ENTRY_DIGEST_SHA1             0x00000003\n#define ARCHIVE_ENTRY_DIGEST_SHA256           0x00000004\n#define ARCHIVE_ENTRY_DIGEST_SHA384           0x00000005\n#define ARCHIVE_ENTRY_DIGEST_SHA512           0x00000006\n\n__LA_DECL const unsigned char * archive_entry_digest(struct archive_entry *, int /* type */);\n\n/*\n * ACL routines.  This used to simply store and return text-format ACL\n * strings, but that proved insufficient for a number of reasons:\n *   = clients need control over uname/uid and gname/gid mappings\n *   = there are many different ACL text formats\n *   = would like to be able to read/convert archives containing ACLs\n *     on platforms that lack ACL libraries\n *\n *  This last point, in particular, forces me to implement a reasonably\n *  complete set of ACL support routines.\n */\n\n/*\n * Permission bits.\n */\n#define\tARCHIVE_ENTRY_ACL_EXECUTE             0x00000001\n#define\tARCHIVE_ENTRY_ACL_WRITE               0x00000002\n#define\tARCHIVE_ENTRY_ACL_READ                0x00000004\n#define\tARCHIVE_ENTRY_ACL_READ_DATA           0x00000008\n#define\tARCHIVE_ENTRY_ACL_LIST_DIRECTORY      0x00000008\n#define\tARCHIVE_ENTRY_ACL_WRITE_DATA          0x00000010\n#define\tARCHIVE_ENTRY_ACL_ADD_FILE            0x00000010\n#define\tARCHIVE_ENTRY_ACL_APPEND_DATA         0x00000020\n#define\tARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY    0x00000020\n#define\tARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS    0x00000040\n#define\tARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS   0x00000080\n#define\tARCHIVE_ENTRY_ACL_DELETE_CHILD        0x00000100\n#define\tARCHIVE_ENTRY_ACL_READ_ATTRIBUTES     0x00000200\n#define\tARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES    0x00000400\n#define\tARCHIVE_ENTRY_ACL_DELETE              0x00000800\n#define\tARCHIVE_ENTRY_ACL_READ_ACL            0x00001000\n#define\tARCHIVE_ENTRY_ACL_WRITE_ACL           0x00002000\n#define\tARCHIVE_ENTRY_ACL_WRITE_OWNER         0x00004000\n#define\tARCHIVE_ENTRY_ACL_SYNCHRONIZE         0x00008000\n\n#define\tARCHIVE_ENTRY_ACL_PERMS_POSIX1E\t\t\t\\\n\t(ARCHIVE_ENTRY_ACL_EXECUTE\t\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_WRITE\t\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_READ)\n\n#define ARCHIVE_ENTRY_ACL_PERMS_NFS4\t\t\t\\\n\t(ARCHIVE_ENTRY_ACL_EXECUTE\t\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_READ_DATA\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY \t\t\\\n\t    | ARCHIVE_ENTRY_ACL_WRITE_DATA\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_ADD_FILE\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_APPEND_DATA\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY\t\\\n\t    | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS\t\\\n\t    | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS\t\\\n\t    | ARCHIVE_ENTRY_ACL_DELETE_CHILD\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES\t\\\n\t    | ARCHIVE_ENTRY_ACL_DELETE\t\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_READ_ACL\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_WRITE_ACL\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_WRITE_OWNER\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_SYNCHRONIZE)\n\n/*\n * Inheritance values (NFS4 ACLs only); included in permset.\n */\n#define\tARCHIVE_ENTRY_ACL_ENTRY_INHERITED                   0x01000000\n#define\tARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT                0x02000000\n#define\tARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT           0x04000000\n#define\tARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT        0x08000000\n#define\tARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY                0x10000000\n#define\tARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS           0x20000000\n#define\tARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS               0x40000000\n\n#define\tARCHIVE_ENTRY_ACL_INHERITANCE_NFS4\t\t\t\\\n\t(ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT\t\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT\t\\\n\t    | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS\t\t\\\n\t    | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED)\n\n/* We need to be able to specify combinations of these. */\n#define\tARCHIVE_ENTRY_ACL_TYPE_ACCESS\t0x00000100  /* POSIX.1e only */\n#define\tARCHIVE_ENTRY_ACL_TYPE_DEFAULT\t0x00000200  /* POSIX.1e only */\n#define\tARCHIVE_ENTRY_ACL_TYPE_ALLOW\t0x00000400 /* NFS4 only */\n#define\tARCHIVE_ENTRY_ACL_TYPE_DENY\t0x00000800 /* NFS4 only */\n#define\tARCHIVE_ENTRY_ACL_TYPE_AUDIT\t0x00001000 /* NFS4 only */\n#define\tARCHIVE_ENTRY_ACL_TYPE_ALARM\t0x00002000 /* NFS4 only */\n#define\tARCHIVE_ENTRY_ACL_TYPE_POSIX1E\t(ARCHIVE_ENTRY_ACL_TYPE_ACCESS \\\n\t    | ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)\n#define\tARCHIVE_ENTRY_ACL_TYPE_NFS4\t(ARCHIVE_ENTRY_ACL_TYPE_ALLOW \\\n\t    | ARCHIVE_ENTRY_ACL_TYPE_DENY \\\n\t    | ARCHIVE_ENTRY_ACL_TYPE_AUDIT \\\n\t    | ARCHIVE_ENTRY_ACL_TYPE_ALARM)\n\n/* Tag values mimic POSIX.1e */\n#define\tARCHIVE_ENTRY_ACL_USER\t\t10001\t/* Specified user. */\n#define\tARCHIVE_ENTRY_ACL_USER_OBJ \t10002\t/* User who owns the file. */\n#define\tARCHIVE_ENTRY_ACL_GROUP\t\t10003\t/* Specified group. */\n#define\tARCHIVE_ENTRY_ACL_GROUP_OBJ\t10004\t/* Group who owns the file. */\n#define\tARCHIVE_ENTRY_ACL_MASK\t\t10005\t/* Modify group access (POSIX.1e only) */\n#define\tARCHIVE_ENTRY_ACL_OTHER\t\t10006\t/* Public (POSIX.1e only) */\n#define\tARCHIVE_ENTRY_ACL_EVERYONE\t10107   /* Everyone (NFS4 only) */\n\n/*\n * Set the ACL by clearing it and adding entries one at a time.\n * Unlike the POSIX.1e ACL routines, you must specify the type\n * (access/default) for each entry.  Internally, the ACL data is just\n * a soup of entries.  API calls here allow you to retrieve just the\n * entries of interest.  This design (which goes against the spirit of\n * POSIX.1e) is useful for handling archive formats that combine\n * default and access information in a single ACL list.\n */\n__LA_DECL void\t archive_entry_acl_clear(struct archive_entry *);\n__LA_DECL int\t archive_entry_acl_add_entry(struct archive_entry *,\n\t    int /* type */, int /* permset */, int /* tag */,\n\t    int /* qual */, const char * /* name */);\n__LA_DECL int\t archive_entry_acl_add_entry_w(struct archive_entry *,\n\t    int /* type */, int /* permset */, int /* tag */,\n\t    int /* qual */, const wchar_t * /* name */);\n\n/*\n * To retrieve the ACL, first \"reset\", then repeatedly ask for the\n * \"next\" entry.  The want_type parameter allows you to request only\n * certain types of entries.\n */\n__LA_DECL int\t archive_entry_acl_reset(struct archive_entry *, int /* want_type */);\n__LA_DECL int\t archive_entry_acl_next(struct archive_entry *, int /* want_type */,\n\t    int * /* type */, int * /* permset */, int * /* tag */,\n\t    int * /* qual */, const char ** /* name */);\n\n/*\n * Construct a text-format ACL.  The flags argument is a bitmask that\n * can include any of the following:\n *\n * Flags only for archive entries with POSIX.1e ACL:\n * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include POSIX.1e \"access\" entries.\n * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include POSIX.1e \"default\" entries.\n * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include \"default:\" before each\n *    default ACL entry.\n * ARCHIVE_ENTRY_ACL_STYLE_SOLARIS - Output only one colon after \"other\" and\n *    \"mask\" entries.\n *\n * Flags only for archive entries with NFSv4 ACL:\n * ARCHIVE_ENTRY_ACL_STYLE_COMPACT - Do not output the minus character for\n *    unset permissions and flags in NFSv4 ACL permission and flag fields\n *\n * Flags for for archive entries with POSIX.1e ACL or NFSv4 ACL:\n * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in\n *    each ACL entry.\n * ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA - Separate entries with comma\n *    instead of newline.\n */\n#define\tARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID\t0x00000001\n#define\tARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT\t0x00000002\n#define\tARCHIVE_ENTRY_ACL_STYLE_SOLARIS\t\t0x00000004\n#define\tARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA\t0x00000008\n#define\tARCHIVE_ENTRY_ACL_STYLE_COMPACT\t\t0x00000010\n\n__LA_DECL wchar_t *archive_entry_acl_to_text_w(struct archive_entry *,\n\t    la_ssize_t * /* len */, int /* flags */);\n__LA_DECL char *archive_entry_acl_to_text(struct archive_entry *,\n\t    la_ssize_t * /* len */, int /* flags */);\n__LA_DECL int archive_entry_acl_from_text_w(struct archive_entry *,\n\t    const wchar_t * /* wtext */, int /* type */);\n__LA_DECL int archive_entry_acl_from_text(struct archive_entry *,\n\t    const char * /* text */, int /* type */);\n\n/* Deprecated constants */\n#define\tOLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID\t\t1024\n#define\tOLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT\t2048\n\n/* Deprecated functions */\n__LA_DECL const wchar_t\t*archive_entry_acl_text_w(struct archive_entry *,\n\t\t    int /* flags */) __LA_DEPRECATED;\n__LA_DECL const char *archive_entry_acl_text(struct archive_entry *,\n\t\t    int /* flags */) __LA_DEPRECATED;\n\n/* Return bitmask of ACL types in an archive entry */\n__LA_DECL int\t archive_entry_acl_types(struct archive_entry *);\n\n/* Return a count of entries matching 'want_type' */\n__LA_DECL int\t archive_entry_acl_count(struct archive_entry *, int /* want_type */);\n\n/* Return an opaque ACL object. */\n/* There's not yet anything clients can actually do with this... */\nstruct archive_acl;\n__LA_DECL struct archive_acl *archive_entry_acl(struct archive_entry *);\n\n/*\n * extended attributes\n */\n\n__LA_DECL void\t archive_entry_xattr_clear(struct archive_entry *);\n__LA_DECL void\t archive_entry_xattr_add_entry(struct archive_entry *,\n\t    const char * /* name */, const void * /* value */,\n\t    size_t /* size */);\n\n/*\n * To retrieve the xattr list, first \"reset\", then repeatedly ask for the\n * \"next\" entry.\n */\n\n__LA_DECL int\tarchive_entry_xattr_count(struct archive_entry *);\n__LA_DECL int\tarchive_entry_xattr_reset(struct archive_entry *);\n__LA_DECL int\tarchive_entry_xattr_next(struct archive_entry *,\n\t    const char ** /* name */, const void ** /* value */, size_t *);\n\n/*\n * sparse\n */\n\n__LA_DECL void\t archive_entry_sparse_clear(struct archive_entry *);\n__LA_DECL void\t archive_entry_sparse_add_entry(struct archive_entry *,\n\t    la_int64_t /* offset */, la_int64_t /* length */);\n\n/*\n * To retrieve the xattr list, first \"reset\", then repeatedly ask for the\n * \"next\" entry.\n */\n\n__LA_DECL int\tarchive_entry_sparse_count(struct archive_entry *);\n__LA_DECL int\tarchive_entry_sparse_reset(struct archive_entry *);\n__LA_DECL int\tarchive_entry_sparse_next(struct archive_entry *,\n\t    la_int64_t * /* offset */, la_int64_t * /* length */);\n\n/*\n * Utility to match up hardlinks.\n *\n * The 'struct archive_entry_linkresolver' is a cache of archive entries\n * for files with multiple links.  Here's how to use it:\n *   1. Create a lookup object with archive_entry_linkresolver_new()\n *   2. Tell it the archive format you're using.\n *   3. Hand each archive_entry to archive_entry_linkify().\n *      That function will return 0, 1, or 2 entries that should\n *      be written.\n *   4. Call archive_entry_linkify(resolver, NULL) until\n *      no more entries are returned.\n *   5. Call archive_entry_linkresolver_free(resolver) to free resources.\n *\n * The entries returned have their hardlink and size fields updated\n * appropriately.  If an entry is passed in that does not refer to\n * a file with multiple links, it is returned unchanged.  The intention\n * is that you should be able to simply filter all entries through\n * this machine.\n *\n * To make things more efficient, be sure that each entry has a valid\n * nlinks value.  The hardlink cache uses this to track when all links\n * have been found.  If the nlinks value is zero, it will keep every\n * name in the cache indefinitely, which can use a lot of memory.\n *\n * Note that archive_entry_size() is reset to zero if the file\n * body should not be written to the archive.  Pay attention!\n */\nstruct archive_entry_linkresolver;\n\n/*\n * There are three different strategies for marking hardlinks.\n * The descriptions below name them after the best-known\n * formats that rely on each strategy:\n *\n * \"Old cpio\" is the simplest, it always returns any entry unmodified.\n *    As far as I know, only cpio formats use this.  Old cpio archives\n *    store every link with the full body; the onus is on the dearchiver\n *    to detect and properly link the files as they are restored.\n * \"tar\" is also pretty simple; it caches a copy the first time it sees\n *    any link.  Subsequent appearances are modified to be hardlink\n *    references to the first one without any body.  Used by all tar\n *    formats, although the newest tar formats permit the \"old cpio\" strategy\n *    as well.  This strategy is very simple for the dearchiver,\n *    and reasonably straightforward for the archiver.\n * \"new cpio\" is trickier.  It stores the body only with the last\n *    occurrence.  The complication is that we might not\n *    see every link to a particular file in a single session, so\n *    there's no easy way to know when we've seen the last occurrence.\n *    The solution here is to queue one link until we see the next.\n *    At the end of the session, you can enumerate any remaining\n *    entries by calling archive_entry_linkify(NULL) and store those\n *    bodies.  If you have a file with three links l1, l2, and l3,\n *    you'll get the following behavior if you see all three links:\n *           linkify(l1) => NULL   (the resolver stores l1 internally)\n *           linkify(l2) => l1     (resolver stores l2, you write l1)\n *           linkify(l3) => l2, l3 (all links seen, you can write both).\n *    If you only see l1 and l2, you'll get this behavior:\n *           linkify(l1) => NULL\n *           linkify(l2) => l1\n *           linkify(NULL) => l2   (at end, you retrieve remaining links)\n *    As the name suggests, this strategy is used by newer cpio variants.\n *    It's noticeably more complex for the archiver, slightly more complex\n *    for the dearchiver than the tar strategy, but makes it straightforward\n *    to restore a file using any link by simply continuing to scan until\n *    you see a link that is stored with a body.  In contrast, the tar\n *    strategy requires you to rescan the archive from the beginning to\n *    correctly extract an arbitrary link.\n */\n\n__LA_DECL struct archive_entry_linkresolver *archive_entry_linkresolver_new(void);\n__LA_DECL void archive_entry_linkresolver_set_strategy(\n\tstruct archive_entry_linkresolver *, int /* format_code */);\n__LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *);\n__LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *,\n    struct archive_entry **, struct archive_entry **);\n__LA_DECL struct archive_entry *archive_entry_partial_links(\n    struct archive_entry_linkresolver *res, unsigned int *links);\n#ifdef __cplusplus\n}\n#endif\n\n/* This is meaningless outside of this header. */\n#undef __LA_DECL\n\n#endif /* !ARCHIVE_ENTRY_H_INCLUDED */\n"
  },
  {
    "path": "Sources/ContainerizationArchive/TempDir.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationExtras\nimport Foundation\n\ninternal func createTemporaryDirectory(baseName: String) -> URL? {\n    let url = FileManager.default.uniqueTemporaryDirectory().appendingPathComponent(\n        \"\\(baseName).XXXXXX\")\n\n    var path = url.absoluteURL.path\n    return path.withUTF8 { utf8Bytes in\n        var mutablePath = Array(utf8Bytes) + [0]\n        return mutablePath.withUnsafeMutableBufferPointer { buffer -> URL? in\n            guard let baseAddress = buffer.baseAddress else { return nil }\n            mkdtemp(baseAddress)\n            let resultPath = String(decoding: buffer[..<(buffer.count - 1)], as: UTF8.self)\n            return URL(fileURLWithPath: resultPath, isDirectory: true)\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationArchive/WriteEntry.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport CArchive\nimport Foundation\n\n/// Represents a single entry (e.g., a file, directory, symbolic link)\n/// that is to be read/written into an archive.\npublic final class WriteEntry {\n    let underlying: OpaquePointer\n\n    public init(_ archive: ArchiveWriter) {\n        underlying = archive_entry_new2(archive.underlying)\n    }\n\n    public init() {\n        underlying = archive_entry_new()\n    }\n\n    deinit {\n        archive_entry_free(underlying)\n    }\n}\n\nextension WriteEntry {\n    /// The size of the entry in bytes.\n    public var size: Int64? {\n        get {\n            guard archive_entry_size_is_set(underlying) != 0 else { return nil }\n            return archive_entry_size(underlying)\n        }\n        set {\n            if let s = newValue {\n                archive_entry_set_size(underlying, s)\n            } else {\n                archive_entry_unset_size(underlying)\n            }\n        }\n    }\n\n    /// The mode of the entry.\n    public var permissions: mode_t {\n        get {\n            archive_entry_perm(underlying)\n        }\n        set {\n            archive_entry_set_perm(underlying, newValue)\n        }\n    }\n\n    /// The owner id of the entry.\n    public var owner: uid_t? {\n        get {\n            uid_t(exactly: archive_entry_uid(underlying))\n        }\n        set {\n            archive_entry_set_uid(underlying, Int64(newValue ?? 0))\n        }\n    }\n\n    /// The group id of the entry\n    public var group: gid_t? {\n        get {\n            gid_t(exactly: archive_entry_gid(underlying))\n        }\n        set {\n            archive_entry_set_gid(underlying, Int64(newValue ?? 0))\n        }\n    }\n\n    /// The path of file this entry hardlinks to\n    public var hardlink: String? {\n        get {\n            guard let cstr = archive_entry_hardlink(underlying) else {\n                return nil\n            }\n            return String(cString: cstr)\n        }\n        set {\n            guard let newValue else {\n                archive_entry_set_hardlink(underlying, nil)\n                return\n            }\n            newValue.withCString {\n                archive_entry_set_hardlink(underlying, $0)\n            }\n        }\n    }\n\n    /// The UTF-8 encoded path of file this entry hardlinks to\n    public var hardlinkUtf8: String? {\n        get {\n            guard let cstr = archive_entry_hardlink_utf8(underlying) else {\n                return nil\n            }\n            return String(cString: cstr, encoding: .utf8)\n        }\n        set {\n            guard let newValue else {\n                archive_entry_set_hardlink_utf8(underlying, nil)\n                return\n            }\n            newValue.withCString {\n                archive_entry_set_hardlink_utf8(underlying, $0)\n            }\n        }\n    }\n\n    /// The string representation of the permissions of the entry\n    public var strmode: String? {\n        if let cstr = archive_entry_strmode(underlying) {\n            return String(cString: cstr)\n        }\n        return nil\n    }\n\n    /// The type of file this entry represents.\n    public var fileType: URLFileResourceType {\n        get {\n            switch archive_entry_filetype(underlying) {\n            case S_IFIFO: return .namedPipe\n            case S_IFCHR: return .characterSpecial\n            case S_IFDIR: return .directory\n            case S_IFBLK: return .blockSpecial\n            case S_IFREG: return .regular\n            case S_IFLNK: return .symbolicLink\n            case S_IFSOCK: return .socket\n            default: return .unknown\n            }\n        }\n        set {\n            switch newValue {\n            case .namedPipe: archive_entry_set_filetype(underlying, UInt32(S_IFIFO as mode_t))\n            case .characterSpecial: archive_entry_set_filetype(underlying, UInt32(S_IFCHR as mode_t))\n            case .directory: archive_entry_set_filetype(underlying, UInt32(S_IFDIR as mode_t))\n            case .blockSpecial: archive_entry_set_filetype(underlying, UInt32(S_IFBLK as mode_t))\n            case .regular: archive_entry_set_filetype(underlying, UInt32(S_IFREG as mode_t))\n            case .symbolicLink: archive_entry_set_filetype(underlying, UInt32(S_IFLNK as mode_t))\n            case .socket: archive_entry_set_filetype(underlying, UInt32(S_IFSOCK as mode_t))\n            default: archive_entry_set_filetype(underlying, 0)\n            }\n        }\n    }\n\n    /// The date that the entry was last accessed\n    public var contentAccessDate: Date? {\n        get {\n            Date(\n                underlying,\n                archive_entry_atime_is_set,\n                archive_entry_atime,\n                archive_entry_atime_nsec)\n        }\n        set {\n            setDate(\n                newValue,\n                underlying, archive_entry_set_atime,\n                archive_entry_unset_atime)\n        }\n    }\n\n    /// The date that the entry was created\n    public var creationDate: Date? {\n        get {\n            Date(\n                underlying,\n                archive_entry_ctime_is_set,\n                archive_entry_ctime,\n                archive_entry_ctime_nsec)\n        }\n        set {\n            setDate(\n                newValue,\n                underlying, archive_entry_set_ctime,\n                archive_entry_unset_ctime)\n        }\n    }\n\n    /// The date that the entry was modified\n    public var modificationDate: Date? {\n        get {\n            Date(\n                underlying,\n                archive_entry_mtime_is_set,\n                archive_entry_mtime,\n                archive_entry_mtime_nsec)\n        }\n        set {\n            setDate(\n                newValue,\n                underlying, archive_entry_set_mtime,\n                archive_entry_unset_mtime)\n        }\n    }\n\n    /// The file path of the entry\n    public var path: String? {\n        get {\n            guard let pathname = archive_entry_pathname(underlying) else {\n                return nil\n            }\n            return String(cString: pathname)\n        }\n        set {\n            guard let newValue else {\n                archive_entry_set_pathname(underlying, nil)\n                return\n            }\n            newValue.withCString {\n                archive_entry_set_pathname(underlying, $0)\n            }\n        }\n    }\n\n    /// The UTF-8 encoded file path of the entry\n    public var pathUtf8: String? {\n        get {\n            guard let pathname = archive_entry_pathname_utf8(underlying) else {\n                return nil\n            }\n            return String(cString: pathname)\n        }\n        set {\n            guard let newValue else {\n                archive_entry_set_pathname_utf8(underlying, nil)\n                return\n            }\n            newValue.withCString {\n                archive_entry_set_pathname_utf8(underlying, $0)\n            }\n        }\n    }\n\n    /// The symlink target that the entry points to\n    public var symlinkTarget: String? {\n        get {\n            guard let target = archive_entry_symlink(underlying) else {\n                return nil\n            }\n            return String(cString: target)\n        }\n        set {\n            guard let newValue else {\n                archive_entry_set_symlink(underlying, nil)\n                return\n            }\n            newValue.withCString {\n                archive_entry_set_symlink(underlying, $0)\n            }\n        }\n    }\n\n    /// The extended attributes of the entry\n    public var xattrs: [String: Data] {\n        get {\n            archive_entry_xattr_reset(self.underlying)\n            var attrs: [String: Data] = [:]\n            var namePtr: UnsafePointer<CChar>?\n            var valuePtr: UnsafeRawPointer?\n            var size: Int = 0\n            while archive_entry_xattr_next(self.underlying, &namePtr, &valuePtr, &size) == 0 {\n                let _name = namePtr.map { String(cString: $0) }\n                let _value = valuePtr.map { Data(bytes: $0, count: size) }\n                guard let name = _name, let value = _value else {\n                    continue\n                }\n                attrs[name] = value\n            }\n            return attrs\n        }\n        set {\n            archive_entry_xattr_clear(self.underlying)\n            for (key, value) in newValue {\n                value.withUnsafeBytes { ptr in\n                    archive_entry_xattr_add_entry(self.underlying, key, ptr.baseAddress, [UInt8](value).count)\n                }\n            }\n        }\n    }\n\n    fileprivate func setDate(\n        _ date: Date?, _ underlying: OpaquePointer, _ setter: (OpaquePointer, time_t, CLong) -> Void,\n        _ unset: (OpaquePointer) -> Void\n    ) {\n        if let d = date {\n            let ti = d.timeIntervalSince1970\n            let seconds = floor(ti)\n            let nsec = max(0, min(1_000_000_000, ti - seconds * 1_000_000_000))\n            setter(underlying, time_t(seconds), CLong(nsec))\n        } else {\n            unset(underlying)\n        }\n    }\n}\n\nextension Date {\n    init?(\n        _ underlying: OpaquePointer, _ isSet: (OpaquePointer) -> CInt, _ seconds: (OpaquePointer) -> time_t,\n        _ nsec: (OpaquePointer) -> CLong\n    ) {\n        guard isSet(underlying) != 0 else { return nil }\n        let ti = TimeInterval(seconds(underlying)) + TimeInterval(nsec(underlying)) * 0.000_000_001\n        self.init(timeIntervalSince1970: ti)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/Documentation.docc/ext4.md",
    "content": "# ``ContainerizationEXT4``\n\n`ContainerizationEXT4` provides functionality to read the superblock of an existing ext4 block device and format a new block device with\nthe ext4 file system.\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/EXT4+Extensions.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\nextension EXT4.InodeFlag {\n    public static func | (lhs: Self, rhs: Self) -> Self {\n        Self(rawValue: lhs.rawValue | rhs.rawValue)\n    }\n\n    public static func | (lhs: Self, rhs: Self) -> UInt32 {\n        lhs.rawValue | rhs.rawValue\n    }\n\n    public static func | (lhs: Self, rhs: UInt32) -> UInt32 {\n        lhs.rawValue | rhs\n    }\n}\n\nextension EXT4.CompatFeature {\n    public static func | (lhs: Self, rhs: Self) -> Self {\n        EXT4.CompatFeature(rawValue: lhs.rawValue | rhs.rawValue)\n    }\n\n    public static func | (lhs: Self, rhs: Self) -> UInt32 {\n        lhs.rawValue | rhs.rawValue\n    }\n}\n\nextension EXT4.IncompatFeature {\n    public static func | (lhs: Self, rhs: Self) -> Self {\n        EXT4.IncompatFeature(rawValue: lhs.rawValue | rhs.rawValue)\n    }\n\n    public static func | (lhs: Self, rhs: Self) -> UInt32 {\n        lhs.rawValue | rhs.rawValue\n    }\n}\n\nextension EXT4.RoCompatFeature {\n    public static func | (lhs: Self, rhs: Self) -> Self {\n        EXT4.RoCompatFeature(rawValue: lhs.rawValue | rhs.rawValue)\n    }\n\n    public static func | (lhs: Self, rhs: Self) -> UInt32 {\n        lhs.rawValue | rhs.rawValue\n    }\n}\n\nextension EXT4.FileModeFlag {\n    public static func | (lhs: Self, rhs: Self) -> Self {\n        Self(rawValue: lhs.rawValue | rhs.rawValue)\n    }\n\n    public static func | (lhs: Self, rhs: Self) -> UInt16 {\n        lhs.rawValue | rhs.rawValue\n    }\n}\n\nextension EXT4.XAttrEntry {\n    init(using bytes: [UInt8]) throws {\n        guard bytes.count == 16 else {\n            throw EXT4.Error.invalidXattrEntry\n        }\n        nameLength = bytes[0]\n        nameIndex = bytes[1]\n        let rawValue = Array(bytes[2...3])\n        valueOffset = UInt16(littleEndian: rawValue.withUnsafeBytes { $0.load(as: UInt16.self) })\n\n        let rawValueInum = Array(bytes[4...7])\n        valueInum = UInt32(littleEndian: rawValueInum.withUnsafeBytes { $0.load(as: UInt32.self) })\n\n        let rawSize = Array(bytes[8...11])\n        valueSize = UInt32(littleEndian: rawSize.withUnsafeBytes { $0.load(as: UInt32.self) })\n\n        let rawHash = Array(bytes[12...])\n        hash = UInt32(littleEndian: rawHash.withUnsafeBytes { $0.load(as: UInt32.self) })\n    }\n}\n\nextension EXT4 {\n    static func tupleToArray<T>(_ tuple: T) -> [UInt8] {\n        let reflection = Mirror(reflecting: tuple)\n        return reflection.children.compactMap { $0.value as? UInt8 }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/EXT4+FileTree.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport SystemPackage\n\nextension EXT4 {\n    class FileTree {\n        class FileTreeNode {\n            let inode: InodeNumber\n            let name: String\n            var children: [Ptr<FileTreeNode>] = []\n            var blocks: (start: UInt32, end: UInt32)?\n            var additionalBlocks: [(start: UInt32, end: UInt32)]?\n            var link: InodeNumber?\n            private var parent: Ptr<FileTreeNode>?\n\n            init(\n                inode: InodeNumber,\n                name: String,\n                parent: Ptr<FileTreeNode>?,\n                children: [Ptr<FileTreeNode>] = [],\n                blocks: (start: UInt32, end: UInt32)? = nil,\n                additionalBlocks: [(start: UInt32, end: UInt32)]? = nil,\n                link: InodeNumber? = nil\n            ) {\n                self.inode = inode\n                self.name = name\n                self.children = children\n                self.blocks = blocks\n                self.additionalBlocks = additionalBlocks\n                self.link = link\n                self.parent = parent\n            }\n\n            deinit {\n                self.children.removeAll()\n                self.children = []\n                self.blocks = nil\n                self.additionalBlocks = nil\n                self.link = nil\n            }\n\n            var path: FilePath? {\n                var components: [String] = [self.name]\n                var _ptr = self.parent\n                while let ptr = _ptr {\n                    components.append(ptr.pointee.name)\n                    _ptr = ptr.pointee.parent\n                }\n                guard let last = components.last else {\n                    return nil\n                }\n                guard components.count > 1 else {\n                    return FilePath(last)\n                }\n                components = components.dropLast()\n                let path = components.reversed().joined(separator: \"/\")\n                guard let data = path.data(using: .utf8) else {\n                    return nil\n                }\n                guard let dataPath = String(data: data, encoding: .utf8) else {\n                    return nil\n                }\n                return FilePath(dataPath).pushing(FilePath(last)).lexicallyNormalized()\n            }\n        }\n\n        var root: Ptr<FileTreeNode>\n\n        init(_ root: InodeNumber, _ name: String) {\n            self.root = Ptr<FileTreeNode>.allocate(capacity: 1)\n            self.root.initialize(to: FileTreeNode(inode: root, name: name, parent: nil))\n        }\n\n        func lookup(path: FilePath) -> Ptr<FileTreeNode>? {\n            var components: [String] = path.items\n            var node = self.root\n            if components.first == \"/\" {\n                components = Array(components.dropFirst())\n            }\n            if components.count == 0 {\n                return node\n            }\n            for component in components {\n                var found = false\n                for childPtr in node.pointee.children {\n                    let child = childPtr.pointee\n                    if child.name == component {\n                        node = childPtr\n                        found = true\n                        break\n                    }\n                }\n                guard found else {\n                    return nil\n                }\n            }\n            return node\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/EXT4+Formatter.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//  swiftlint: disable discouraged_direct_init shorthand_operator syntactic_sugar\n\nimport ContainerizationArchive\nimport ContainerizationOS\nimport Foundation\nimport SystemPackage\n\nextension EXT4 {\n    /// The `EXT4.Formatter` class provides methods to format a block device with the ext4 filesystem.\n    /// It allows customization of block size and maximum disk size.\n    public class Formatter {\n        let blockSize: UInt32\n        private var size: UInt64\n        private let groupDescriptorSize: UInt32 = 32\n\n        private var blocksPerGroup: UInt32 {\n            blockSize * 8\n        }\n\n        private var maxInodesPerGroup: UInt32 {\n            blockSize * 8  // limited by inode bitmap\n        }\n\n        private var groupsPerDescriptorBlock: UInt32 {\n            blockSize / groupDescriptorSize\n        }\n\n        private var blockCount: UInt32 {\n            ((size - 1) / blockSize) + 1\n        }\n\n        private var groupCount: UInt32 {\n            (blockCount - 1) / blocksPerGroup + 1\n        }\n\n        private var groupDescriptorBlocks: UInt32 {\n            ((groupCount - 1) / groupsPerDescriptorBlock + 1) * 32\n        }\n\n        /// Initializes an ext4 filesystem formatter.\n        ///\n        /// This constructor creates an instance of the ext4 formatter designed to format a block device\n        /// with the ext4 filesystem. The formatter takes the path to the destination block device and\n        /// the desired block size of the filesystem as parameters.\n        ///\n        /// - Parameters:\n        ///   - devicePath: The path to the block device where the ext4 filesystem will be created.\n        ///   - blockSize: The block size of the ext4 filesystem, specified in bytes. Common values are\n        ///                4096 (4KB) or 1024 (1KB). Default is 4096 (4KB)\n        ///   - minDiskSize: The minimum disk size required for the formatted filesystem.\n        ///\n        /// - Note: This ext4 formatter is designed for creating block devices out of container images and does not support all the\n        ///         features and options available in the full ext4 filesystem implementation. It focuses\n        ///         on the core functionality required for formatting a block device with ext4.\n        ///\n        /// - Important: Ensure that the destination block device is accessible and has sufficient permissions\n        ///              for formatting. The formatting process will erase all existing data on the device.\n        public init(_ devicePath: FilePath, blockSize: UInt32 = 4096, minDiskSize: UInt64 = 256.kib()) throws {\n            /// The constructor performs the following steps:\n            ///\n            /// 1. Creates the first 10 inodes:\n            ///    - Inode 2 is reserved for the root directory ('/').\n            ///    - Inodes 1 and 3-10 are reserved for other special purposes.\n            ///\n            /// 2. Marks inode 11 as the first inode available for consumption by files, directories, sockets,\n            ///    FIFOs, etc.\n            ///\n            /// 3. Initializes a directory tree with the root directory pointing to inode 2.\n            ///\n            /// 4. Moves the file descriptor to the start of the block where file metadata and data can be\n            ///    written, which is located past the filesystem superblocks and group descriptor blocks.\n            ///\n            /// 5. Creates a \"/lost+found\" directory to satisfy the requirements of e2fsck (ext2/3/4 filesystem\n            ///    checker).\n\n            if !FileManager.default.fileExists(atPath: devicePath.description) {\n                FileManager.default.createFile(atPath: devicePath.description, contents: nil)\n            }\n            guard let fileHandle = FileHandle(forWritingTo: devicePath) else {\n                throw Error.notFound(devicePath)\n            }\n            self.handle = fileHandle\n            self.blockSize = blockSize\n            self.size = minDiskSize\n            // make this a 0 byte file\n            guard ftruncate(self.handle.fileDescriptor, 0) == 0 else {\n                throw Error.cannotTruncateFile(devicePath)\n            }\n            // make it a sparse file\n            guard lseek(self.handle.fileDescriptor, off_t(self.size - 1), 0) == self.size - 1 else {\n                throw Error.cannotCreateSparseFile(devicePath)\n            }\n            let zero: [UInt8] = [0]\n            try self.handle.write(contentsOf: zero)\n            // step #1\n            self.inodes = [\n                Ptr<Inode>.allocate(capacity: 1),  // defective block inode\n                {\n                    let root = Inode.Root()\n                    let rootPtr = Ptr<Inode>.allocate(capacity: 1)\n                    rootPtr.initialize(to: root)\n                    return rootPtr\n                }(),\n            ]\n            // reserved inodes\n            for _ in 2..<EXT4.FirstInode - 1 {\n                inodes.append(Ptr<Inode>.allocate(capacity: 1))\n            }\n            // step #2\n            self.tree = FileTree(EXT4.RootInode, \"/\")\n            // skip past the superblock and block descriptor table\n            try self.seek(block: self.groupDescriptorBlocks + 1)\n            // lost+found directory is required for e2fsck to pass\n            try self.create(path: FilePath(\"/lost+found\"), mode: Inode.Mode(.S_IFDIR, 0o700))\n        }\n\n        // Creates a hard link at the path specified by `link` that points to the same file or directory as the path specified by `target`.\n        //\n        // A hard link is a directory entry that points to the same inode as another directory entry. It allows multiple paths to refer to the same file on the file system.\n        //\n        // - `link`: The path at which to create the new hard link.\n        // - `target`: The path of the existing file or directory to which the hard link should point.\n        //\n        // Throws an error if `target` path does not exist, or `target` is a directory.\n        public func link(\n            link: FilePath,\n            target: FilePath\n        ) throws {\n            // ensure that target exists\n            guard let targetPtr = self.tree.lookup(path: target) else {\n                throw Error.notFound(target)\n            }\n            let targetNode = targetPtr.pointee\n            let targetInodePtr = self.inodes[Int(targetNode.inode) - 1]\n            var targetInode = targetInodePtr.pointee\n            // ensure that target is not a directory since hardlinks cannot be\n            // created to directories\n            if targetInode.mode.isDir() {\n                throw Error.cannotCreateHardlinksToDirTarget(link)\n            }\n            targetInode.linksCount += 1\n            targetInodePtr.initialize(to: targetInode)\n            let parentPath: FilePath = link.dir\n            if self.tree.lookup(path: link) != nil {\n                try self.unlink(path: link)\n            }\n            guard let parentTreeNodePtr = self.tree.lookup(path: parentPath) else {\n                throw Error.notFound(parentPath)\n            }\n            let parentTreeNode = parentTreeNodePtr.pointee\n            let parentInodePtr = self.inodes[Int(parentTreeNode.inode) - 1]\n            let parentInode = parentInodePtr.pointee\n            guard parentInode.linksCount < EXT4.MaxLinks else {\n                throw Error.maximumLinksExceeded(parentPath)\n            }\n            let linkTreeNodePtr = Ptr<FileTree.FileTreeNode>.allocate(capacity: 1)\n            let linkTreeNode = FileTree.FileTreeNode(\n                inode: InodeNumber(2),  // this field is ignored, using 2 so array operations dont panic\n                name: link.base,\n                parent: parentTreeNodePtr,\n                children: [],\n                blocks: nil,\n                link: targetNode.inode\n            )\n            linkTreeNodePtr.initialize(to: linkTreeNode)\n            parentTreeNode.children.append(linkTreeNodePtr)\n            parentTreeNodePtr.initialize(to: parentTreeNode)\n        }\n\n        // Deletes the file or directory at the specified path from the filesystem.\n        //\n        // It performs the following actions\n        // - set link count of the file's inode to 0\n        // - recursively set link count to 0 for its children\n        // - free the inode\n        // - free data blocks\n        // - remove directory entry\n        //\n        // - `path`: The `FilePath` specifying the path of the file or directory to delete.\n        public func unlink(path: FilePath, directoryWhiteout: Bool = false) throws {\n            guard let pathPtr = self.tree.lookup(path: path) else {\n                // We are being asked to unlink something that does not exist. Ignore\n                return\n            }\n            let pathNode = pathPtr.pointee\n            let inodeNumber = Int(pathNode.inode) - 1\n            let pathInodePtr = self.inodes[inodeNumber]\n            var pathInode = pathInodePtr.pointee\n\n            if directoryWhiteout && !pathInode.mode.isDir() {\n                throw Error.notDirectory(path)\n            }\n\n            for childPtr in pathNode.children {\n                try self.unlink(path: path.join(childPtr.pointee.name))\n            }\n\n            guard !directoryWhiteout else {\n                return\n            }\n\n            if let parentNodePtr = self.tree.lookup(path: path.dir) {\n                let parentNode = parentNodePtr.pointee\n                let parentInodePtr = self.inodes[Int(parentNode.inode) - 1]\n                var parentInode = parentInodePtr.pointee\n                if pathInode.mode.isDir() {\n                    if parentInode.linksCount > 2 {\n                        parentInode.linksCount -= 1\n                    }\n                }\n                parentInodePtr.initialize(to: parentInode)\n                parentNode.children.removeAll { childPtr in\n                    childPtr.pointee.name == path.base\n                }\n                parentNodePtr.initialize(to: parentNode)\n            }\n\n            if let hardlink = pathNode.link {\n                // the file we are deleting is a hardlink, decrement the link count\n                let linkedInodePtr = self.inodes[Int(hardlink - 1)]\n                var linkedInode = linkedInodePtr.pointee\n                if linkedInode.linksCount > 2 {\n                    linkedInode.linksCount -= 1\n                    linkedInodePtr.initialize(to: linkedInode)\n                }\n            }\n\n            guard inodeNumber > FirstInode else {\n                // Free the inodes and the blocks related to the inode only if its valid\n                return\n            }\n            if let blocks = pathNode.blocks {\n                if !(blocks.start == blocks.end) {\n                    self.deletedBlocks.append((start: blocks.start, end: blocks.end))\n                }\n            }\n            for block in pathNode.additionalBlocks ?? [] {\n                self.deletedBlocks.append((start: block.start, end: block.end))\n            }\n            let now = Date().fs()\n            pathInode = Inode()\n            pathInode.dtime = now.lo\n            pathInodePtr.initialize(to: pathInode)\n        }\n\n        //  Creates a file, directory, or symlink at the specified path, recursively creating parent directories if they don't already exist.\n        //\n        //  - Parameters:\n        //    - path: The FilePath representing the path where the file, directory, or symlink should be created.\n        //    - link: An optional FilePath representing the target path for a symlink. If `nil`, a regular file or directory will be created. Preceding '/' should be omitted\n        //    - mode: The permissions to set for the created file, directory, or symlink.\n        //    - buf: A `ReadableStream` object providing the contents for the created file. Ignored when creating directories or symlinks.\n        //\n        //  - Note:\n        //    - This function recursively creates parent directories if they don't already exist. The `uid` and `gid` of the created parent directories are set to the values of their parent's `uid` and `gid`.\n        //    - It is expected that the user sets the permissions explicitly later\n        //    - This function only supports creating files, directories, and symlinks. Attempting to create other types of file system objects will result in an error.\n        //    - In case of symlinks, the preceding '/' should be omitted\n        //\n        //  - Example usage:\n        //    ```swift\n        //     let formatter = EXT4.Formatter(devicePath: \"ext4.img\")\n        //     // create a directory\n        //     try formatter.create(path: FilePath(\"/dir\"),\n        //         mode: EXT4.Inode.Mode(.S_IFDIR, 0o700))\n        //\n        //     // create a file\n        //     let inputStream = InputStream(data: \"data\".data(using: .utf8)!)\n        //     inputStream.open()\n        //     try formatter.create(path: FilePath(\"/dir/file\"),\n        //         mode: EXT4.Inode.Mode(.S_IFREG, 0o755), buf: inputStream)\n        //     inputStream.close()\n        //\n        //     // create a symlink\n        //     try formatter.create(path: FilePath(\"/symlink\"), link: \"/dir/file\",\n        //         mode: EXT4.Inode.Mode(.S_IFLNK, 0o700))\n        //    ```\n        public func create(\n            path: FilePath,\n            link: FilePath? = nil,  // to create symbolic links\n            mode: UInt16,\n            ts: FileTimestamps = FileTimestamps(),\n            buf: (any ReadableStream)? = nil,\n            uid: UInt32? = nil,\n            gid: UInt32? = nil,\n            xattrs: [String: Data]? = nil,\n            recursion: Bool = false,\n            fileBuffer: UnsafeMutableBufferPointer<UInt8>? = nil\n        ) throws {\n            if let nodePtr = self.tree.lookup(path: path) {\n                let node = nodePtr.pointee\n                let inodePtr = self.inodes[Int(node.inode) - 1]\n                let inode = inodePtr.pointee\n                // Allowed replace\n                // -----------------------------\n                //\n                // Original Type    File    Directory    Symlink\n                // ----------------------------------------------\n                // File           |  ✔    |     ✘      |     ✔\n                // Directory      |  ✘    |     ✔      |     ✔\n                // Symlink        |  ✔    |     ✘      |     ✔\n                if mode.isDir() {\n                    if !inode.mode.isDir() {\n                        guard inode.mode.isLink() else {\n                            throw Error.notDirectory(path)\n                        }\n                    }\n                    // mkdir -p\n                    if path.base == node.name {\n                        guard !recursion else {\n                            return\n                        }\n                        // create a new tree node to replace this one\n                        var inode = inode\n                        inode.mode = mode\n                        if let uid {\n                            inode.uid = uid.lo\n                            inode.uidHigh = uid.hi\n                        }\n                        if let gid {\n                            inode.gid = gid.lo\n                            inode.gidHigh = gid.hi\n                        }\n                        inodePtr.initialize(to: inode)\n                        return\n                    }\n                } else if let _ = node.link {  // ok to overwrite links\n                    try self.unlink(path: path)\n                } else {  // file can only be overwritten by another file\n                    if inode.mode.isDir() {\n                        guard mode.isLink() else {  // unless it is a link, then it can be replaced by a dir\n                            throw Error.notFile(path)\n                        }\n                    }\n                    try self.unlink(path: path)\n                }\n            }\n            // create all predecessors recursively\n            let parentPath: FilePath = path.dir\n            try self.create(path: parentPath, mode: Inode.Mode(.S_IFDIR, 0o755), recursion: true)\n            guard let parentTreeNodePtr = self.tree.lookup(path: parentPath) else {\n                throw Error.notFound(parentPath)\n            }\n            let parentTreeNode = parentTreeNodePtr.pointee\n            let parentInodePtr = self.inodes[Int(parentTreeNode.inode) - 1]\n            var parentInode = parentInodePtr.pointee\n            guard parentInode.linksCount < EXT4.MaxLinks else {\n                throw Error.maximumLinksExceeded(parentPath)\n            }\n\n            let childInodePtr = Ptr<Inode>.allocate(capacity: 1)\n            var childInode = Inode()\n            var startBlock: UInt32 = 0\n            var endBlock: UInt32 = 0\n            defer {  // update metadata\n                childInodePtr.initialize(to: childInode)\n                parentInodePtr.initialize(to: parentInode)\n                self.inodes.append(childInodePtr)\n                let childTreeNodePtr = Ptr<FileTree.FileTreeNode>.allocate(capacity: 1)\n                let childTreeNode = FileTree.FileTreeNode(\n                    inode: InodeNumber(self.inodes.count),\n                    name: path.base,\n                    parent: parentTreeNodePtr,\n                    children: [],\n                    blocks: (startBlock, endBlock)\n                )\n                childTreeNodePtr.initialize(to: childTreeNode)\n                parentTreeNode.children.append(childTreeNodePtr)\n                parentTreeNodePtr.initialize(to: parentTreeNode)\n            }\n            childInode.mode = mode\n            // uid,gid\n            if let uid {\n                childInode.uid = UInt16(uid & 0xffff)\n                childInode.uidHigh = UInt16((uid >> 16) & 0xffff)\n            } else {\n                childInode.uid = parentInode.uid\n                childInode.uidHigh = parentInode.uidHigh\n            }\n            if let gid {\n                childInode.gid = UInt16(gid & 0xffff)\n                childInode.gidHigh = UInt16((gid >> 16) & 0xffff)\n            } else {\n                childInode.gid = parentInode.gid\n                childInode.gidHigh = parentInode.gidHigh\n            }\n            if let xattrs, !xattrs.isEmpty {\n                var state = FileXattrsState(\n                    inode: UInt32(self.inodes.count), inodeXattrCapacity: EXT4.InodeExtraSize, blockCapacity: blockSize)\n                try state.add(ExtendedAttribute(name: \"system.data\", value: []))\n                for (s, d) in xattrs {\n                    let attribute = ExtendedAttribute(name: s, value: [UInt8](d))\n                    try state.add(attribute)\n                }\n                if !state.inlineAttributes.isEmpty {\n                    var buffer: [UInt8] = .init(repeating: 0, count: Int(EXT4.InodeExtraSize))\n                    try state.writeInlineAttributes(buffer: &buffer)\n                    childInode.inlineXattrs = (\n                        buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7],\n                        buffer[8],\n                        buffer[9],\n                        buffer[10], buffer[11], buffer[12], buffer[13], buffer[14], buffer[15], buffer[16], buffer[17],\n                        buffer[18],\n                        buffer[19],\n                        buffer[20], buffer[21], buffer[22], buffer[23], buffer[24], buffer[25], buffer[26], buffer[27],\n                        buffer[28],\n                        buffer[29],\n                        buffer[30], buffer[31], buffer[32], buffer[33], buffer[34], buffer[35], buffer[36], buffer[37],\n                        buffer[38],\n                        buffer[39],\n                        buffer[40], buffer[41], buffer[42], buffer[43], buffer[44], buffer[45], buffer[46], buffer[47],\n                        buffer[48],\n                        buffer[49],\n                        buffer[50], buffer[51], buffer[52], buffer[53], buffer[54], buffer[55], buffer[56], buffer[57],\n                        buffer[58],\n                        buffer[59],\n                        buffer[60], buffer[61], buffer[62], buffer[63], buffer[64], buffer[65], buffer[66], buffer[67],\n                        buffer[68],\n                        buffer[69],\n                        buffer[70], buffer[71], buffer[72], buffer[73], buffer[74], buffer[75], buffer[76], buffer[77],\n                        buffer[78],\n                        buffer[79],\n                        buffer[80], buffer[81], buffer[82], buffer[83], buffer[84], buffer[85], buffer[86], buffer[87],\n                        buffer[88],\n                        buffer[89],\n                        buffer[90], buffer[91], buffer[92], buffer[93], buffer[94], buffer[95]\n                    )\n                }\n                if !state.blockAttributes.isEmpty {\n                    var buffer: [UInt8] = .init(repeating: 0, count: Int(blockSize))\n                    try state.writeBlockAttributes(buffer: &buffer)\n                    if self.pos % self.blockSize != 0 {\n                        try self.seek(block: self.currentBlock + 1)\n                    }\n                    childInode.xattrBlockLow = self.currentBlock\n                    try self.handle.write(contentsOf: buffer)\n                    childInode.blocksLow += 1\n                }\n            }\n\n            childInode.atime = ts.accessLo\n            childInode.atimeExtra = ts.accessHi\n            // ctime is the last time the inode was changed which is now\n            childInode.ctime = ts.nowLo\n            childInode.ctimeExtra = ts.nowHi\n            childInode.mtime = ts.modificationLo\n            childInode.mtimeExtra = ts.modificationHi\n            childInode.crtime = ts.creationLo\n            childInode.crtimeExtra = ts.creationHi\n            childInode.linksCount = 1\n            childInode.extraIsize = UInt16(EXT4.ExtraIsize)\n            // flags\n            childInode.flags = InodeFlag.hugeFile.rawValue\n            // size check\n            var size: UInt64 = 0\n            // align with block boundary\n            if self.pos % self.blockSize != 0 {\n                try self.seek(block: self.currentBlock + 1)\n            }\n            // dir\n            if childInode.mode.isDir() {\n                childInode.linksCount += 1\n                parentInode.linksCount += 1\n                // to pass e2fsck, the convention is to sort children\n                // before committing to disk. Therefore, we are deferring\n                // writing dentries until commit() is called\n                return\n            }\n            // symbolic link\n            if let link {\n                startBlock = self.currentBlock\n                let linkPath = link.bytes\n                if linkPath.count < 60 {\n                    size += UInt64(linkPath.count)\n                    var blockData: [UInt8] = .init(repeating: 0, count: 60)\n                    for i in 0..<linkPath.count {\n                        blockData[i] = linkPath[i]\n                    }\n                    childInode.block = (\n                        blockData[0], blockData[1], blockData[2], blockData[3], blockData[4], blockData[5],\n                        blockData[6],\n                        blockData[7], blockData[8], blockData[9],\n                        blockData[10], blockData[11], blockData[12], blockData[13], blockData[14], blockData[15],\n                        blockData[16],\n                        blockData[17], blockData[18], blockData[19],\n                        blockData[20], blockData[21], blockData[22], blockData[23], blockData[24], blockData[25],\n                        blockData[26],\n                        blockData[27], blockData[28], blockData[29],\n                        blockData[30], blockData[31], blockData[32], blockData[33], blockData[34], blockData[35],\n                        blockData[36],\n                        blockData[37], blockData[38], blockData[39],\n                        blockData[40], blockData[41], blockData[42], blockData[43], blockData[44], blockData[45],\n                        blockData[46],\n                        blockData[47], blockData[48], blockData[49],\n                        blockData[50], blockData[51], blockData[52], blockData[53], blockData[54], blockData[55],\n                        blockData[56],\n                        blockData[57], blockData[58], blockData[59]\n                    )\n                } else {\n                    try linkPath.withUnsafeBytes { buffer in\n                        try withUnsafeLittleEndianBuffer(of: buffer) { b in\n                            try self.handle.write(contentsOf: b)\n                        }\n                        size += UInt64(buffer.count)\n                    }\n                }\n                if self.pos % self.blockSize != 0 {\n                    try self.seek(block: self.currentBlock + 1)\n                }\n                endBlock = self.currentBlock\n                childInode.sizeLow = size.lo\n                childInode.sizeHigh = size.hi\n                childInode.mode |= 0o777\n                childInode.flags = 0\n                if linkPath.count < 60 {\n                    childInode.blocksLow = 0\n                } else {\n                    childInode = try self.writeExtents(childInode, (startBlock, endBlock))\n                    childInode.blocksLow = 8\n                }\n                return\n            }\n            // regular file\n            if mode.isReg() {\n                startBlock = self.currentBlock\n                if let buf {  // in case of empty files, this will be nil\n                    let tempBuf: UnsafeMutablePointer<UInt8>\n                    let bufferSize: Int\n                    let shouldDeallocate: Bool\n                    if let fileBuffer {\n                        tempBuf = fileBuffer.baseAddress!\n                        bufferSize = fileBuffer.count\n                        shouldDeallocate = false\n                    } else {\n                        tempBuf = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(self.blockSize))\n                        bufferSize = Int(self.blockSize)\n                        shouldDeallocate = true\n                    }\n                    defer {\n                        if shouldDeallocate {\n                            tempBuf.deallocate()\n                        }\n                    }\n                    while case let block = buf.read(tempBuf, maxLength: bufferSize), block > 0 {\n                        size += UInt64(block)\n                        if size > EXT4.MaxFileSize {\n                            throw Error.fileTooBig(size)\n                        }\n                        let data = UnsafeRawBufferPointer(start: tempBuf, count: block)\n                        try withUnsafeLittleEndianBuffer(of: data) { b in\n                            try self.handle.write(contentsOf: b)\n                        }\n                    }\n                }\n                if self.pos % self.blockSize != 0 {\n                    try self.seek(block: self.currentBlock + 1)\n                }\n                endBlock = self.currentBlock\n                childInode.sizeLow = size.lo\n                childInode.sizeHigh = size.hi\n                childInode = try self.writeExtents(childInode, (startBlock, endBlock))\n                return\n            }\n            // FIFO, Socket and other types are not handled\n            throw Error.unsupportedFiletype\n        }\n\n        //  Completes the formatting of an ext4 filesystem after writing the necessary structures.\n        //\n        //  This function is responsible for finalizing the formatting process of an ext4 filesystem\n        //  after the following structures have been written:\n        //  - Inode table: Contains information about each file and directory in the filesystem.\n        //  - Block bitmap: Tracks the allocation status of each block in the filesystem.\n        //  - Inode bitmap: Tracks the allocation status of each inode in the filesystem.\n        //  - Directory tree: Represents the hierarchical structure of directories and files.\n        //  - Group descriptors: Stores metadata about each block group in the filesystem.\n        //  - Superblock: Contains essential information about the filesystem's configuration.\n        //\n        //  The function performs any necessary final steps to ensure the integrity and consistency\n        //  of the ext4 filesystem before it can be mounted and used.\n        public func close() throws {\n            var breathWiseChildTree: [(parent: Ptr<FileTree.FileTreeNode>?, child: Ptr<FileTree.FileTreeNode>)] = [\n                (nil, self.tree.root)\n            ]\n            while !breathWiseChildTree.isEmpty {\n                let (parent, child) = breathWiseChildTree.removeFirst()\n                try self.commit(parent, child)  // commit directories iteratively\n                if child.pointee.link != nil {\n                    continue\n                }\n                breathWiseChildTree.append(contentsOf: child.pointee.children.map { (child, $0) })\n            }\n            let blockGroupSize = optimizeBlockGroupLayout(blocks: self.currentBlock, inodes: UInt32(self.inodes.count))\n            let inodeTableOffset = try self.commitInodeTable(\n                blockGroups: blockGroupSize.blockGroups,\n                inodesPerGroup: blockGroupSize.inodesPerGroup\n            )\n            if self.pos % self.blockSize != 0 {\n                try self.seek(block: self.currentBlock + 1)\n            }\n            // write bitmaps and group descriptors\n\n            let bitmapOffset = self.currentBlock\n            let bitmapSize: UInt32 = blockGroupSize.blockGroups * 2  // each group has two bitmaps - for inodes, and for blocks\n            let dataSize: UInt32 = bitmapOffset + bitmapSize  // last data block\n            var diskSize = dataSize\n            var minimumDiskSize = (blockGroupSize.blockGroups - 1) * self.blocksPerGroup + 1\n            if blockGroupSize.blockGroups == 1 {\n                minimumDiskSize = self.blocksPerGroup  // at least 1 block group\n            }\n            if diskSize < minimumDiskSize {  // for data + metadata\n                diskSize = minimumDiskSize\n            }\n            if self.size < minimumDiskSize {\n                self.size = UInt64(minimumDiskSize) * self.blockSize\n            }\n            // number of blocks needed for group descriptors\n            let groupDescriptorBlockCount: UInt32 = (blockGroupSize.blockGroups - 1) / self.groupsPerDescriptorBlock + 1\n            guard groupDescriptorBlockCount <= self.groupDescriptorBlocks else {\n                throw Error.insufficientSpaceForGroupDescriptorBlocks\n            }\n\n            var totalBlocks: UInt32 = 0\n            var totalInodes: UInt32 = 0\n            let inodeTableSizePerGroup: UInt32 = blockGroupSize.inodesPerGroup * EXT4.InodeSize / self.blockSize\n            var groupDescriptors: [GroupDescriptor] = []\n\n            let minGroups = (((self.pos / UInt64(self.blockSize)) - 1) / UInt64(self.blocksPerGroup)) + 1\n            if self.size < minGroups * blocksPerGroup * blockSize {\n                self.size = UInt64(minGroups * blocksPerGroup * blockSize)\n                let pos = self.pos\n                guard lseek(self.handle.fileDescriptor, off_t(self.size - 1), 0) == self.size - 1 else {\n                    throw Error.cannotResizeFS(self.size)\n                }\n                let zero: [UInt8] = [0]\n                try self.handle.write(contentsOf: zero)\n                try self.handle.seek(toOffset: pos)\n            }\n            let totalGroups = (((self.size / UInt64(self.blockSize)) - 1) / UInt64(self.blocksPerGroup)) + 1\n\n            // If the provided disk size is not aligned to a blockgroup boundary, it needs to\n            // be expanded to the next blockgroup boundary.\n            // Example:\n            //  Provided disk size: 2 GB + 100MB: 2148 MB\n            //  BlockSize: 4096\n            //  Blockgroup size: 32768 blocks: 128MB\n            //  Number of blocks: 549888\n            //  Number of blockgroups = 549888 / 32768 = 16.78125\n            //  Aligned disk size = 557056 blocks = 17 blockgroups: 2176 MB\n            if self.size < totalGroups * blocksPerGroup * blockSize {\n                self.size = UInt64(totalGroups * blocksPerGroup * blockSize)\n                let pos = self.pos\n                guard lseek(self.handle.fileDescriptor, off_t(self.size - 1), 0) == self.size - 1 else {\n                    throw Error.cannotResizeFS(self.size)\n                }\n                let zero: [UInt8] = [0]\n                try self.handle.write(contentsOf: zero)\n                try self.handle.seek(toOffset: pos)\n            }\n            for group in 0..<blockGroupSize.blockGroups {\n                // keep track of directories, inodes and block per blockgroup\n                var dirs: UInt32 = 0\n                var inodes: UInt32 = 0\n                var blocks: UInt32 = 0\n                // blocks bitmap\n                var bitmap: [UInt8] = .init(repeating: 0, count: self.blockSize * 2)  // 1 for blocks, 1 for inodes\n                if (group + 1) * UInt32(self.blocksPerGroup) <= dataSize {  // fully allocated group\n                    for i in 0..<(self.blockSize) {\n                        bitmap[Int(i)] = 0xff  // mark as allocated\n                    }\n                    blocks = UInt32(self.blocksPerGroup)\n                } else if group * UInt32(self.blocksPerGroup) < dataSize {  // partially allocated group\n                    for i in 0..<dataSize - group * UInt32(self.blocksPerGroup) {\n                        bitmap[Int(i / 8)] |= 1 << (i % 8)\n                        blocks += 1\n                    }\n                }\n\n                if group == 0 {  // unused group descriptor blocks\n                    // blocks used by group descriptors\n\n                    let usedGroupDescriptorBlocks = (totalGroups - 1) / self.groupsPerDescriptorBlock + 1\n                    for i in 0...usedGroupDescriptorBlocks {\n                        bitmap[Int(i / 8)] |= 1 << (i % 8)\n                    }\n                    for i in usedGroupDescriptorBlocks + 1...self.groupDescriptorBlocks {\n                        bitmap[Int(i / 8)] &= ~(1 << (i % 8))\n                        blocks -= 1\n                    }\n                }\n\n                // last blockGroup if not aligned with total size should be marked as allocated\n                let remainingBlocks = diskSize % self.blocksPerGroup\n                if group == totalGroups - 1 && remainingBlocks != 0 && self.size / self.blockSize < self.blocksPerGroup {\n                    for i in remainingBlocks..<self.blocksPerGroup {\n                        bitmap[Int(i / 8)] |= 1 << (i % 8)\n                    }\n                    if remainingBlocks < self.size / self.blockSize {\n                        for i in remainingBlocks..<self.size / self.blockSize {\n                            bitmap[Int(i / 8)] &= ~(1 << (i % 8))\n                        }\n                    }\n                }\n\n                // mark deleted blocks as free\n                for block in self.deletedBlocks {\n                    for i in block.start..<block.end where i / self.blocksPerGroup == group {\n                        let j = i % self.blocksPerGroup\n                        blocks -= UInt32((bitmap[Int(j / 8)] >> (j % 8)) & 1)\n                        bitmap[Int(j / 8)] &= ~(1 << (j % 8))\n                    }\n                }\n\n                // inodes bitmap goes into second bitmap block\n                for i in 0..<blockGroupSize.inodesPerGroup {\n                    let ino = InodeNumber(1 + group * blockGroupSize.inodesPerGroup + i)\n                    if ino > self.inodes.count {\n                        continue\n                    }\n                    let inode = self.inodes[Int(ino) - 1]\n                    if ino > 10 && inode.pointee.linksCount == 0 {  // deleted files\n                        continue\n                    }\n                    bitmap[Int(self.blockSize) + Int(i / 8)] |= 1 << (i % 8)\n                    inodes += 1\n                    if inode.pointee.mode.isDir() {\n                        dirs += 1\n                    }\n                }\n\n                for i in (blockGroupSize.inodesPerGroup / 8)..<self.blockSize {\n                    bitmap[Int(self.blockSize) + Int(i)] = 0xff  // mark rest of inodes as occupied\n                }\n\n                // write bitmap\n                try bitmap.withUnsafeBytes { bitmapBytes in\n                    try withUnsafeLittleEndianBuffer(of: bitmapBytes) { b in\n                        try self.handle.write(contentsOf: b)\n                    }\n                }\n\n                var freeBlocks: UInt32 = UInt32(self.blocksPerGroup)\n                if freeBlocks < blocks {\n                    freeBlocks = 0\n                } else if self.size / self.blockSize < self.blocksPerGroup {\n                    if blocks < UInt32(self.size / UInt64(self.blockSize)) {\n                        freeBlocks = UInt32(self.size / UInt64(self.blockSize)) - blocks\n                    } else {\n                        freeBlocks = 0\n                    }\n                } else {\n                    freeBlocks = UInt32(self.blocksPerGroup) - blocks\n                }\n\n                let blockBitmap = UInt64(bitmapOffset + 2 * group)\n                let inodeBitmap = UInt64(bitmapOffset + 2 * group + 1)\n                let inodeTable = inodeTableOffset + UInt64(group * inodeTableSizePerGroup)\n                let freeBlocksCount = UInt32(self.blocksPerGroup - blocks)\n                let freeInodesCount = UInt32(blockGroupSize.inodesPerGroup - inodes)\n                groupDescriptors.append(\n                    // low bits\n                    GroupDescriptor(\n                        blockBitmapLow: blockBitmap.lo,  // address of block bitmap\n                        inodeBitmapLow: inodeBitmap.lo,  // address of inode bitmap\n                        inodeTableLow: inodeTable.lo,  // address of inode table for this group\n                        freeBlocksCountLow: freeBlocksCount.lo,\n                        freeInodesCountLow: freeInodesCount.lo,\n                        usedDirsCountLow: dirs.lo,\n                        flags: 0x0000,\n                        excludeBitmapLow: 0x0000_0000,\n                        blockBitmapCsumLow: 0x0000,\n                        inodeBitmapCsumLow: 0x0000,\n                        itableUnusedLow: 0x0000,\n                        checksum: 0x0000\n                    ))\n                totalBlocks += UInt32(blocks)\n                totalInodes += UInt32(inodes)\n            }\n\n            // Since the bitmaps for unoccupied block groups are the same, there is no need\n            // to allocate separate memory or storage for each individual bitmap.\n            var blockBitmap: [UInt8] = .init(repeating: 0, count: Int(self.blocksPerGroup) / 8)\n            var inodeBitmap: [UInt8] = .init(repeating: 0xff, count: Int(self.blocksPerGroup) / 8)\n            for i in 0..<inodeTableSizePerGroup + 2 {\n                blockBitmap[Int(i) / 8] |= 1 << (i % 8)\n            }\n            for i in 0..<UInt16(blockGroupSize.inodesPerGroup) {\n                inodeBitmap[Int(i) / 8] &= ~(1 << (i % 8))\n            }\n            for group in blockGroupSize.blockGroups..<totalGroups.lo {\n                var blocksInGroup = UInt32(self.blocksPerGroup)\n                if group == totalGroups.lo {\n                    if UInt64(self.size / UInt64(self.blockSize)) < self.blocksPerGroup {\n                        break\n                    }\n                    blocksInGroup = UInt32((self.size / UInt64(self.blockSize)) % UInt64(self.blocksPerGroup))\n                    if blocksInGroup == 0 {\n                        break\n                    }\n                }\n                let blockBitmapOffset = UInt64(group * self.blocksPerGroup + inodeTableSizePerGroup)\n                let inodeBitmapOffset = UInt64(group * self.blocksPerGroup + inodeTableSizePerGroup + 1)\n                let inodeTableOffset = UInt64(self.blocksPerGroup) * group\n                let freeBlocksCount = UInt32(blocksInGroup - inodeTableSizePerGroup - 2)\n                let freeInodesCount = UInt32(blockGroupSize.inodesPerGroup)\n                groupDescriptors.append(\n                    // low bits\n                    GroupDescriptor(\n                        blockBitmapLow: blockBitmapOffset.lo,  // address of block bitmap\n                        inodeBitmapLow: inodeBitmapOffset.lo,  // address of inode bitmap\n                        inodeTableLow: inodeTableOffset.lo,  // address of inode table for this group\n                        freeBlocksCountLow: freeBlocksCount.lo,\n                        freeInodesCountLow: freeInodesCount.lo,\n                        usedDirsCountLow: 0,\n                        flags: 0x0000,\n                        excludeBitmapLow: 0x0000_0000,\n                        blockBitmapCsumLow: 0x0000,\n                        inodeBitmapCsumLow: 0x0000,\n                        itableUnusedLow: 0x0000,\n                        checksum: 0x0000\n                    ))\n                totalBlocks += (inodeTableSizePerGroup + 2)\n                try self.seek(block: group * self.blocksPerGroup + inodeTableSizePerGroup)\n\n                if group == totalGroups.lo {\n                    var blockBitmapLo: [UInt8] = .init(repeating: 0, count: Int(self.blocksPerGroup) / 8)\n                    for i in blocksInGroup..<UInt32(self.blocksPerGroup) {\n                        blockBitmapLo[Int(i) / 8] |= 1 << (i % 8)\n                    }\n                    for i in 0..<inodeTableSizePerGroup + 2 {\n                        blockBitmapLo[Int(i) / 8] |= 1 << (i % 8)\n                    }\n                    try self.handle.write(contentsOf: blockBitmapLo)\n                    try self.handle.write(contentsOf: inodeBitmap)\n                    continue\n                }\n\n                try self.handle.write(contentsOf: blockBitmap)\n                try self.handle.write(contentsOf: inodeBitmap)\n            }\n\n            try self.seek(block: 1)\n\n            for groupDescriptor in groupDescriptors {\n                try withUnsafeLittleEndianBytes(of: groupDescriptor) { bytes in\n                    try self.handle.write(contentsOf: bytes)\n                }\n            }\n            // write superblock\n            try self.seek(block: 0)\n            try self.handle.write(contentsOf: Array<UInt8>.init(repeating: 0, count: 1024))\n\n            let computedInodes = totalGroups * blockGroupSize.inodesPerGroup\n            var blocksCount = totalGroups * self.blocksPerGroup\n            while blocksCount < totalBlocks {\n                blocksCount = UInt64(totalBlocks)\n            }\n            let totalFreeBlocks: UInt64\n            if totalBlocks > blocksCount {\n                totalFreeBlocks = 0\n            } else {\n                totalFreeBlocks = blocksCount - totalBlocks\n            }\n            var superblock = SuperBlock()\n            superblock.inodesCount = computedInodes.lo\n            superblock.blocksCountLow = blocksCount.lo\n            superblock.blocksCountHigh = blocksCount.hi\n            superblock.freeBlocksCountLow = totalFreeBlocks.lo\n            superblock.freeBlocksCountHigh = totalFreeBlocks.hi\n            let freeInodesCount = computedInodes.lo - totalInodes\n            superblock.freeInodesCount = freeInodesCount\n            superblock.firstDataBlock = 0\n            superblock.logBlockSize = 2\n            superblock.logClusterSize = 2\n            superblock.blocksPerGroup = self.blocksPerGroup\n            superblock.clustersPerGroup = self.blocksPerGroup\n            superblock.inodesPerGroup = blockGroupSize.inodesPerGroup\n            superblock.magic = EXT4.SuperBlockMagic\n            superblock.state = 1  // cleanly unmounted\n            superblock.errors = 1  // continue on error\n            superblock.creatorOS = 3  // freeBSD\n            superblock.revisionLevel = 1  // dynamic inode sizes\n            superblock.firstInode = EXT4.FirstInode\n            superblock.lpfInode = EXT4.LostAndFoundInode\n            superblock.inodeSize = UInt16(EXT4.InodeSize)\n            superblock.featureCompat = CompatFeature.sparseSuper2 | CompatFeature.extAttr\n            superblock.featureIncompat =\n                IncompatFeature.filetype | IncompatFeature.extents | IncompatFeature.flexBg\n            superblock.featureRoCompat =\n                RoCompatFeature.largeFile | RoCompatFeature.hugeFile | RoCompatFeature.extraIsize\n            superblock.minExtraIsize = EXT4.ExtraIsize\n            superblock.wantExtraIsize = EXT4.ExtraIsize\n            superblock.logGroupsPerFlex = 31\n            superblock.uuid = UUID().uuid\n            try withUnsafeLittleEndianBytes(of: superblock) { bytes in\n                try self.handle.write(contentsOf: bytes)\n            }\n            try self.handle.write(contentsOf: Array<UInt8>.init(repeating: 0, count: 2048))\n        }\n\n        // MARK: Private Methods and Properties\n        private var handle: FileHandle\n        private var inodes: [Ptr<Inode>]\n        private var tree: FileTree\n        private var deletedBlocks: [(start: UInt32, end: UInt32)] = []\n\n        private var pos: UInt64 {\n            guard let offset = try? self.handle.offset() else {\n                return 0\n            }\n            return offset\n        }\n\n        private var currentBlock: UInt32 {\n            self.pos / self.blockSize\n        }\n\n        private func seek(block: UInt32) throws {\n            try self.handle.seek(toOffset: UInt64(block) * blockSize)\n        }\n\n        private func commitInodeTable(blockGroups: UInt32, inodesPerGroup: UInt32) throws -> UInt64 {\n            // inodeTable must go into a new block\n            if self.pos % blockSize != 0 {\n                try seek(block: currentBlock + 1)\n            }\n            let inodeTableOffset = UInt64(currentBlock)\n\n            let inodeSize = MemoryLayout<Inode>.size\n            // Write InodeTable\n            for inode in self.inodes {\n                try withUnsafeLittleEndianBytes(of: inode.pointee) { bytes in\n                    try handle.write(contentsOf: bytes)\n                }\n                try self.handle.write(\n                    contentsOf: Array<UInt8>.init(repeating: 0, count: Int(EXT4.InodeSize) - inodeSize))\n            }\n            let tableSize: UInt64 = UInt64(EXT4.InodeSize) * blockGroups * inodesPerGroup\n            let rest = tableSize - uint32(self.inodes.count) * EXT4.InodeSize\n            let zeroBlock = Array<UInt8>.init(repeating: 0, count: Int(self.blockSize))\n            for _ in 0..<(rest / self.blockSize) {\n                try self.handle.write(contentsOf: zeroBlock)\n            }\n            try self.handle.write(contentsOf: Array<UInt8>.init(repeating: 0, count: Int(rest % self.blockSize)))\n            return inodeTableOffset\n        }\n\n        // optimizes the distribution of blockGroups to obtain the lowest number of blockGroups needed to\n        // represent all the inodes and all the blocks in the FS\n        private func optimizeBlockGroupLayout(blocks: UInt32, inodes: UInt32) -> (\n            blockGroups: UInt32, inodesPerGroup: UInt32\n        ) {\n            // counts the number of blockGroups given a particular inodesPerGroup size\n            let groupCount: (_ blocks: UInt32, _ inodes: UInt32, _ inodesPerGroup: UInt32) -> UInt32 = {\n                blocks, inodes, inodesPerGroup in\n                let inodeBlocksPerGroup: UInt32 = inodesPerGroup * EXT4.InodeSize / self.blockSize\n                let dataBlocksPerGroup: UInt32 = self.blocksPerGroup - inodeBlocksPerGroup - 2  // save room for the bitmaps\n                // Increase the block count to ensure there are enough groups for all the inodes.\n                let minBlocks: UInt32 = (inodes - 1) / inodesPerGroup * dataBlocksPerGroup + 1\n                var updatedBlocks = blocks\n                if blocks < minBlocks {\n                    updatedBlocks = minBlocks\n                }\n                return (updatedBlocks + dataBlocksPerGroup - 1) / dataBlocksPerGroup\n            }\n\n            var groups: UInt32 = UInt32.max\n            var inodesPerGroup: UInt32 = 0\n            let inc = Int(self.blockSize * 512) / Int(EXT4.InodeSize)  // inodesPerGroup\n            // minimizes the number of blockGroups needed to its lowest value\n            for ipg in stride(from: inc, through: Int(self.maxInodesPerGroup), by: inc) {\n                let g = groupCount(blocks, inodes, UInt32(ipg))\n                if g < groups {\n                    groups = g\n                    inodesPerGroup = UInt32(ipg)\n                }\n            }\n            return (groups, inodesPerGroup)\n        }\n\n        private func commit(_ parentPtr: Ptr<FileTree.FileTreeNode>?, _ nodePtr: Ptr<FileTree.FileTreeNode>) throws {\n            let node = nodePtr.pointee\n            let inodePtr = self.inodes[Int(node.inode) - 1]\n            var inode = inodePtr.pointee\n            guard inode.linksCount > 0 else {\n                return\n            }\n            if node.link != nil {\n                return\n            }\n            if self.pos % self.blockSize != 0 {\n                try self.seek(block: self.currentBlock + 1)\n            }\n            if inode.mode.isDir() {\n                let startBlock = self.currentBlock\n                var left: Int = Int(self.blockSize)\n                try writeDirEntry(name: \".\", inode: node.inode, left: &left)\n                if let parent = parentPtr {\n                    try writeDirEntry(name: \"..\", inode: parent.pointee.inode, left: &left)\n                } else {\n                    try writeDirEntry(name: \"..\", inode: node.inode, left: &left)\n                }\n                var sortedChildren = Array(node.children)\n                sortedChildren.sort { left, right in\n                    left.pointee.inode < right.pointee.inode\n                }\n                for childPtr in sortedChildren {\n                    let child = childPtr.pointee\n                    try writeDirEntry(name: child.name, inode: child.inode, left: &left, link: child.link)\n                }\n                try finishDirEntryBlock(&left)\n                let endBlock = self.currentBlock\n                let size: UInt64 = UInt64(endBlock - startBlock) * self.blockSize\n                inode.sizeLow = size.lo\n                inode.sizeHigh = size.hi\n                inodePtr.initialize(to: inode)\n                node.blocks = (startBlock, endBlock)\n                nodePtr.initialize(to: node)\n                if self.pos % self.blockSize != 0 {\n                    try self.seek(block: self.currentBlock + 1)\n                }\n                inode = try self.writeExtents(inode, (startBlock, endBlock))\n                inodePtr.initialize(to: inode)\n            }\n        }\n\n        private func fillExtents(\n            node: inout ExtentLeafNode, numExtents: UInt32, numBlocks: UInt32, start: UInt32, offset: UInt32\n        ) {\n            for i in 0..<numExtents {\n                let extentBlock: UInt32 = offset + i * EXT4.MaxBlocksPerExtent\n                var length = numBlocks - extentBlock\n                if length > EXT4.MaxBlocksPerExtent {\n                    length = EXT4.MaxBlocksPerExtent\n                }\n                let extentStart: UInt32 = start + extentBlock\n                let extent = ExtentLeaf(\n                    block: extentBlock,\n                    length: UInt16(length),\n                    startHigh: 0,\n                    startLow: extentStart\n                )\n                node.leaves.append(extent)\n            }\n        }\n\n        private func writeExtents(_ inode: Inode, _ blocks: (start: UInt32, end: UInt32)) throws -> Inode {\n            var inode = inode\n            // rest of code assumes that extents MUST go into a new block\n            if self.pos % self.blockSize != 0 {\n                try self.seek(block: self.currentBlock + 1)\n            }\n            let dataBlocks = blocks.end - blocks.start\n            let numExtents = (dataBlocks + EXT4.MaxBlocksPerExtent - 1) / EXT4.MaxBlocksPerExtent\n            var usedBlocks = dataBlocks\n            let extentNodeSize = 12\n            let extentsPerBlock = self.blockSize / extentNodeSize - 1\n            var blockData: [UInt8] = .init(repeating: 0, count: 60)\n            var blockIndex: Int = 0\n            switch numExtents {\n            case 0:\n                return inode  // noop\n            case 1..<5:\n                let extentHeader = ExtentHeader(\n                    magic: EXT4.ExtentHeaderMagic,\n                    entries: UInt16(numExtents),\n                    max: 4,\n                    depth: 0,\n                    generation: 0)\n\n                var node = ExtentLeafNode(header: extentHeader, leaves: [])\n                fillExtents(node: &node, numExtents: numExtents, numBlocks: dataBlocks, start: blocks.start, offset: 0)\n                withUnsafeLittleEndianBytes(of: node.header) { bytes in\n                    for b in bytes {\n                        blockData[blockIndex] = b\n                        blockIndex = blockIndex + 1\n                    }\n                }\n                for leaf in node.leaves {\n                    withUnsafeLittleEndianBytes(of: leaf) { bytes in\n                        for b in bytes {\n                            blockData[blockIndex] = b\n                            blockIndex = blockIndex + 1\n                        }\n                    }\n                }\n            case 5..<4 * UInt32(extentsPerBlock) + 1:\n                let extentBlocks = numExtents / extentsPerBlock + 1\n                usedBlocks += extentBlocks\n                let extentHeader = ExtentHeader(\n                    magic: EXT4.ExtentHeaderMagic,\n                    entries: UInt16(extentBlocks),\n                    max: 4,\n                    depth: 1,\n                    generation: 0\n                )\n                var root = ExtentIndexNode(header: extentHeader, indices: [])\n                for i in 0..<extentBlocks {\n                    if self.pos % self.blockSize != 0 {\n                        try self.seek(block: self.currentBlock + 1)\n                    }\n                    let extentIdx = ExtentIndex(\n                        block: i * extentsPerBlock * EXT4.MaxBlocksPerExtent,\n                        leafLow: self.currentBlock,\n                        leafHigh: 0,\n                        unused: 0)\n                    var extentsInBlock = numExtents - i * extentsPerBlock\n                    if extentsInBlock > extentsPerBlock {\n                        extentsInBlock = extentsPerBlock\n                    }\n                    let leafHeader = ExtentHeader(\n                        magic: EXT4.ExtentHeaderMagic,\n                        entries: UInt16(extentsInBlock),\n                        max: UInt16(extentsPerBlock),\n                        depth: 0,\n                        generation: 0\n                    )\n                    var leafNode = ExtentLeafNode(header: leafHeader, leaves: [])\n                    let offset = i * extentsPerBlock * EXT4.MaxBlocksPerExtent\n                    fillExtents(\n                        node: &leafNode, numExtents: extentsInBlock, numBlocks: dataBlocks,\n                        start: blocks.start + offset,\n                        offset: offset)\n                    try withUnsafeLittleEndianBytes(of: leafNode.header) { bytes in\n                        try self.handle.write(contentsOf: bytes)\n                    }\n                    for leaf in leafNode.leaves {\n                        try withUnsafeLittleEndianBytes(of: leaf) { bytes in\n                            try self.handle.write(contentsOf: bytes)\n                        }\n                    }\n                    let extentTail = ExtentTail(checksum: leafNode.leaves.last!.block)\n                    try withUnsafeLittleEndianBytes(of: extentTail) { bytes in\n                        try self.handle.write(contentsOf: bytes)\n                    }\n                    root.indices.append(extentIdx)\n                }\n                withUnsafeLittleEndianBytes(of: root.header) { bytes in\n                    for b in bytes {\n                        blockData[blockIndex] = b\n                        blockIndex = blockIndex + 1\n                    }\n                }\n                for leaf in root.indices {\n                    withUnsafeLittleEndianBytes(of: leaf) { bytes in\n                        for b in bytes {\n                            blockData[blockIndex] = b\n                            blockIndex = blockIndex + 1\n                        }\n                    }\n                }\n            default:\n                throw Error.fileTooBig(UInt64(dataBlocks) * self.blockSize)\n            }\n            inode.block = (\n                blockData[0], blockData[1], blockData[2], blockData[3], blockData[4], blockData[5], blockData[6],\n                blockData[7],\n                blockData[8], blockData[9],\n                blockData[10], blockData[11], blockData[12], blockData[13], blockData[14], blockData[15], blockData[16],\n                blockData[17], blockData[18], blockData[19],\n                blockData[20], blockData[21], blockData[22], blockData[23], blockData[24], blockData[25], blockData[26],\n                blockData[27], blockData[28], blockData[29],\n                blockData[30], blockData[31], blockData[32], blockData[33], blockData[34], blockData[35], blockData[36],\n                blockData[37], blockData[38], blockData[39],\n                blockData[40], blockData[41], blockData[42], blockData[43], blockData[44], blockData[45], blockData[46],\n                blockData[47], blockData[48], blockData[49],\n                blockData[50], blockData[51], blockData[52], blockData[53], blockData[54], blockData[55], blockData[56],\n                blockData[57], blockData[58], blockData[59]\n            )\n            // ensure that inode's block count includes extent blocks\n            inode.blocksLow += usedBlocks\n            inode.flags = InodeFlag.extents | inode.flags\n            return inode\n        }\n        // writes a single directory entry\n        private func writeDirEntry(name: String, inode: InodeNumber, left: inout Int, link: InodeNumber? = nil) throws {\n            guard self.inodes[Int(inode) - 1].pointee.linksCount > 0 else {\n                return\n            }\n            guard let nameData = name.data(using: .utf8) else {\n                throw Error.invalidName(name)\n            }\n            let directoryEntrySize = MemoryLayout<DirectoryEntry>.size\n            let rlb = directoryEntrySize + nameData.count\n            let rl = (rlb + 3) & ~3\n            if left < rl + 12 {\n                try self.finishDirEntryBlock(&left)\n            }\n            var mode = self.inodes[Int(inode) - 1].pointee.mode\n            var inodeNum = inode\n            if let link {\n                mode = self.inodes[Int(link) - 1].pointee.mode | 0o777\n                inodeNum = link\n            }\n            let entry = DirectoryEntry(\n                inode: inodeNum,\n                recordLength: UInt16(rl),\n                nameLength: UInt8(nameData.count),\n                fileType: mode.fileType()\n            )\n            try withUnsafeLittleEndianBytes(of: entry) { bytes in\n                try self.handle.write(contentsOf: bytes)\n            }\n\n            try nameData.withUnsafeBytes { buffer in\n                try withUnsafeLittleEndianBuffer(of: buffer) { b in\n                    try self.handle.write(contentsOf: b)\n                }\n            }\n            try self.handle.write(contentsOf: [UInt8](repeating: 0, count: rl - rlb))\n            left = left - rl\n        }\n\n        private func finishDirEntryBlock(_ left: inout Int) throws {\n            defer { left = Int(self.blockSize) }\n            if left <= 0 {\n                return\n            }\n            let entry = DirectoryEntry(\n                inode: InodeNumber(0),\n                recordLength: UInt16(left),\n                nameLength: 0,\n                fileType: 0\n            )\n            try withUnsafeLittleEndianBytes(of: entry) { bytes in\n                try self.handle.write(contentsOf: bytes)\n            }\n            left = left - MemoryLayout<DirectoryEntry>.size\n            if left < 4 {\n                throw Error.noSpaceForTrailingDEntry\n            }\n            try self.handle.write(contentsOf: [UInt8](repeating: 0, count: Int(left)))\n        }\n\n        public enum Error: Swift.Error, CustomStringConvertible, Sendable, Equatable {\n            case notDirectory(_ path: FilePath)\n            case notFile(_ path: FilePath)\n            case notFound(_ path: FilePath)\n            case alreadyExists(_ path: FilePath)\n            case unsupportedFiletype\n            case maximumLinksExceeded(_ path: FilePath)\n            case fileTooBig(_ size: UInt64)\n            case invalidLink(_ path: FilePath)\n            case invalidName(_ name: String)\n            case noSpaceForTrailingDEntry\n            case insufficientSpaceForGroupDescriptorBlocks\n            case cannotCreateHardlinksToDirTarget(_ path: FilePath)\n            case cannotTruncateFile(_ path: FilePath)\n            case cannotCreateSparseFile(_ path: FilePath)\n            case cannotResizeFS(_ size: UInt64)\n            public var description: String {\n                switch self {\n                case .notDirectory(let path):\n                    return \"\\(path) is not a directory\"\n                case .notFile(let path):\n                    return \"\\(path) is not a file\"\n                case .notFound(let path):\n                    return \"\\(path) not found\"\n                case .alreadyExists(let path):\n                    return \"\\(path) already exists\"\n                case .unsupportedFiletype:\n                    return \"file type not supported\"\n                case .maximumLinksExceeded(let path):\n                    return \"maximum links exceeded for path: \\(path)\"\n                case .fileTooBig(let size):\n                    return \"\\(size) exceeds max file size (128 GiB)\"\n                case .invalidLink(let path):\n                    return \"'\\(path)' is an invalid link\"\n                case .invalidName(let name):\n                    return \"'\\(name)' is an invalid name\"\n                case .noSpaceForTrailingDEntry:\n                    return \"not enough space for trailing dentry\"\n                case .insufficientSpaceForGroupDescriptorBlocks:\n                    return \"not enough space for group descriptor blocks\"\n                case .cannotCreateHardlinksToDirTarget(let path):\n                    return \"cannot create hard links to directory target: \\(path)\"\n                case .cannotTruncateFile(let path):\n                    return \"cannot truncate file: \\(path)\"\n                case .cannotCreateSparseFile(let path):\n                    return \"cannot create sparse file at \\(path)\"\n                case .cannotResizeFS(let size):\n                    return \"cannot resize fs to \\(size) bytes\"\n                }\n            }\n        }\n\n        deinit {\n            for inode in inodes {\n                inode.deinitialize(count: 1)\n                inode.deallocate()\n            }\n            self.inodes.removeAll()\n        }\n    }\n}\n\nextension Date {\n    func fs() -> UInt64 {\n        if self == Date.distantPast {\n            return 0\n        }\n\n        let s = self.timeIntervalSince1970\n\n        if s < -0x8000_0000 {\n            return 0x8000_0000\n        }\n\n        if s > 0x3_7fff_ffff {\n            return 0x3_7fff_ffff\n        }\n\n        let seconds = UInt64(s)\n        let nanoseconds = UInt64(self.timeIntervalSince1970.truncatingRemainder(dividingBy: 1) * 1_000_000_000)\n\n        return seconds | (nanoseconds << 34)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/EXT4+Ptr.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\nextension EXT4 {\n    class Ptr<T> {\n        let underlying: UnsafeMutablePointer<T>\n        private var capacity: Int\n        private var initialized: Bool\n        private var allocated: Bool\n\n        var pointee: T {\n            underlying.pointee\n        }\n\n        init(capacity: Int) {\n            self.underlying = UnsafeMutablePointer<T>.allocate(capacity: capacity)\n            self.capacity = capacity\n            self.allocated = true\n            self.initialized = false\n        }\n\n        static func allocate(capacity: Int) -> Ptr<T> {\n            Ptr<T>(capacity: capacity)\n        }\n\n        func initialize(to value: T) {\n            guard self.allocated else {\n                return\n            }\n            if self.initialized {\n                self.underlying.deinitialize(count: self.capacity)\n            }\n            self.underlying.initialize(to: value)\n            self.allocated = true\n            self.initialized = true\n        }\n\n        func deallocate() {\n            guard self.allocated else {\n                return\n            }\n            self.underlying.deallocate()\n            self.allocated = false\n            self.initialized = false\n        }\n\n        func deinitialize(count: Int) {\n            guard self.allocated else {\n                return\n            }\n            guard self.initialized else {\n                return\n            }\n            self.underlying.deinitialize(count: count)\n            self.initialized = false\n            self.allocated = true\n        }\n\n        func move() -> T {\n            self.initialized = false\n            self.allocated = true\n            return self.underlying.move()\n        }\n\n        deinit {\n            self.deinitialize(count: self.capacity)\n            self.deallocate()\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/EXT4+Reader.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport SystemPackage\n\nextension EXT4 {\n    /// The `EXT4Reader` opens a block device, parses the superblock, and loads group descriptors & inodes.\n    public class EXT4Reader {\n        public var superBlock: EXT4.SuperBlock {\n            self._superBlock\n        }\n\n        let handle: FileHandle\n        let _superBlock: EXT4.SuperBlock\n\n        private var groupDescriptors: [UInt32: EXT4.GroupDescriptor] = [:]\n        private var inodes: [InodeNumber: EXT4.Inode] = [:]\n\n        var hardlinks: [FilePath: InodeNumber] = [:]\n        var tree: EXT4.FileTree = EXT4.FileTree(EXT4.RootInode, \".\")\n        var blockSize: UInt64 {\n            UInt64(1024 * (1 << _superBlock.logBlockSize))\n        }\n\n        private var groupDescriptorSize: UInt16 {\n            if _superBlock.featureIncompat & EXT4.IncompatFeature.bit64.rawValue != 0 {\n                return _superBlock.descSize\n            }\n            return UInt16(MemoryLayout<EXT4.GroupDescriptor>.size)\n        }\n\n        public init(blockDevice: FilePath) throws {\n            guard FileManager.default.fileExists(atPath: blockDevice.description) else {\n                throw EXT4.Error.notFound(blockDevice.description)\n            }\n\n            guard let fileHandle = FileHandle(forReadingAtPath: blockDevice) else {\n                throw Error.notFound(blockDevice.description)\n            }\n            self.handle = fileHandle\n            try handle.seek(toOffset: EXT4.SuperBlockOffset)\n\n            let superBlockSize = MemoryLayout<EXT4.SuperBlock>.size\n            guard let data = try? self.handle.read(upToCount: superBlockSize) else {\n                throw EXT4.Error.couldNotReadSuperBlock(blockDevice.description, EXT4.SuperBlockOffset, superBlockSize)\n            }\n            let sb = data.withUnsafeBytes { ptr in\n                ptr.loadLittleEndian(as: EXT4.SuperBlock.self)\n            }\n            guard sb.magic == EXT4.SuperBlockMagic else {\n                throw EXT4.Error.invalidSuperBlock\n            }\n            self._superBlock = sb\n            var items: [(item: Ptr<EXT4.FileTree.FileTreeNode>, inode: InodeNumber)] = [\n                (self.tree.root, EXT4.RootInode)\n            ]\n            while items.count > 0 {\n                guard let item = items.popLast() else {\n                    break\n                }\n                let (itemPtr, inodeNum) = item\n                let childItems = try self.children(of: inodeNum)\n                let root = itemPtr.pointee\n                for (itemName, itemInodeNum) in childItems {\n                    if itemName == \".\" || itemName == \"..\" {\n                        continue\n                    }\n\n                    if self.inodes[itemInodeNum] != nil {\n                        // we have seen this inode before, we will hard link this file to it\n                        guard let parentPath = itemPtr.pointee.path else {\n                            continue\n                        }\n                        let path = parentPath.join(itemName)\n                        self.hardlinks[path] = itemInodeNum\n                        continue\n                    }\n\n                    let blocks = try self.getExtents(inode: itemInodeNum)\n                    let itemTreeNodePtr = Ptr<FileTree.FileTreeNode>.allocate(capacity: 1)\n                    let itemTreeNode = FileTree.FileTreeNode(\n                        inode: itemInodeNum,\n                        name: itemName,\n                        parent: itemPtr,\n                        children: []\n                    )\n                    if let blocks {\n                        if blocks.count > 1 {\n                            itemTreeNode.additionalBlocks = Array(blocks.dropFirst())\n                        }\n                        itemTreeNode.blocks = blocks.first\n                    }\n                    itemTreeNodePtr.initialize(to: itemTreeNode)\n                    root.children.append(itemTreeNodePtr)\n                    itemPtr.initialize(to: root)\n                    let itemInode = try self.getInode(number: itemInodeNum)\n                    if itemInode.mode.isDir() {\n                        items.append((itemTreeNodePtr, itemInodeNum))\n                    }\n                }\n            }\n        }\n\n        deinit {\n            try? self.handle.close()\n        }\n\n        private func readGroupDescriptor(_ number: UInt32) throws -> GroupDescriptor {\n            let bs = UInt64(1024 * (1 << _superBlock.logBlockSize))\n            let offset = bs + UInt64(number) * UInt64(self.groupDescriptorSize)\n            try self.handle.seek(toOffset: offset)\n            guard let data = try? self.handle.read(upToCount: MemoryLayout<EXT4.GroupDescriptor>.size) else {\n                throw EXT4.Error.couldNotReadGroup(number)\n            }\n            let gd = data.withUnsafeBytes { ptr in\n                ptr.loadLittleEndian(as: EXT4.GroupDescriptor.self)\n            }\n            return gd\n        }\n\n        private func readInode(_ number: UInt32) throws -> Inode {\n            let inodeGroupNumber = ((number - 1) / self._superBlock.inodesPerGroup)\n            let numberInGroup = UInt64((number - 1) % self._superBlock.inodesPerGroup)\n\n            let gd = try getGroupDescriptor(inodeGroupNumber)\n            let inodeTableStart = UInt64(gd.inodeTableLow) * self.blockSize\n\n            let inodeOffset: UInt64 = inodeTableStart + numberInGroup * UInt64(_superBlock.inodeSize)\n            try self.handle.seek(toOffset: inodeOffset)\n            guard let inodeData = try self.handle.read(upToCount: MemoryLayout<EXT4.Inode>.size) else {\n                throw EXT4.Error.couldNotReadInode(number)\n            }\n            let inode = inodeData.withUnsafeBytes { ptr in\n                ptr.loadLittleEndian(as: EXT4.Inode.self)\n            }\n            return inode\n        }\n\n        private func getDirTree(_ number: InodeNumber) throws -> [(String, InodeNumber)] {\n            var children: [(String, InodeNumber)] = []\n            let extents = try getExtents(inode: number) ?? []\n            for (start, end) in extents {\n                try self.seek(block: start)\n                for i in 0..<(end - start) {\n                    guard let dirEntryBlock = try self.handle.read(upToCount: Int(self.blockSize)) else {\n                        throw EXT4.Error.couldNotReadBlock(start + i)\n                    }\n                    let childEntries = try getDirEntries(dirTree: dirEntryBlock)\n                    children.append(contentsOf: childEntries)\n                }\n            }\n            return children.sorted { a, b in\n                a.0 < b.0\n            }\n        }\n\n        private func getDirEntries(dirTree: Data) throws -> [(String, InodeNumber)] {\n            var children: [(String, InodeNumber)] = []\n            var offset = 0\n            while offset < dirTree.count {\n                let length = MemoryLayout<DirectoryEntry>.size\n                let dirEntry = dirTree.subdata(in: offset..<offset + length).withUnsafeBytes {\n                    $0.loadLittleEndian(as: DirectoryEntry.self)\n                }\n                if dirEntry.inode == 0 {\n                    break\n                }\n                let nameData = dirTree.subdata(in: offset + 8..<offset + 8 + Int(dirEntry.nameLength))\n                let name = String(data: nameData, encoding: .utf8) ?? \"\"\n                children.append((name, dirEntry.inode))\n                offset += Int(dirEntry.recordLength)\n            }\n            return children.sorted { a, b in\n                a.0 < b.0\n            }\n        }\n\n        func getExtents(inode: InodeNumber) throws -> [(start: UInt32, end: UInt32)]? {\n            let inode = try self.getInode(number: inode)\n            let inodeBlock = Data(tupleToArray(inode.block))\n            var offset = 0\n            var extents: [(start: UInt32, end: UInt32)] = []\n\n            let extentHeaderSize = MemoryLayout<ExtentHeader>.size\n            let extentIndexSize = MemoryLayout<ExtentIndex>.size\n            let extentLeafSize = MemoryLayout<ExtentLeaf>.size\n            // read extent header\n            let header = inodeBlock.subdata(in: offset..<offset + extentHeaderSize).withUnsafeBytes {\n                $0.loadLittleEndian(as: ExtentHeader.self)\n            }\n            guard header.magic == EXT4.ExtentHeaderMagic else {\n                return []\n            }\n            offset += extentHeaderSize  // Jump to entries\n            switch header.depth {\n            case 0:\n                // When depth is 0 the extent header is followed by extent leaves\n                for _ in 0..<header.entries {\n                    let leaf = inodeBlock.subdata(in: offset..<offset + extentLeafSize).withUnsafeBytes {\n                        $0.load(as: ExtentLeaf.self)\n                    }\n                    extents.append((leaf.startLow, leaf.startLow + UInt32(leaf.length)))\n                    offset += extentLeafSize\n                }\n            case 1:\n                // When depth is 1 the extent header is followed by extent indices which point to leaves\n                for _ in 0..<header.entries {\n                    let indexNode = inodeBlock.subdata(in: offset..<offset + extentIndexSize).withUnsafeBytes {\n                        $0.load(as: ExtentIndex.self)\n                    }\n                    try self.seek(block: indexNode.leafLow)\n                    guard let block = try self.handle.read(upToCount: Int(self.blockSize)) else {\n                        throw EXT4.Error.couldNotReadBlock(indexNode.leafLow)\n                    }\n                    var blockOffset = 0\n                    let leafHeader = block.subdata(in: blockOffset..<extentHeaderSize).withUnsafeBytes {\n                        $0.loadLittleEndian(as: ExtentHeader.self)\n                    }\n                    guard leafHeader.magic == EXT4.ExtentHeaderMagic else {\n                        throw Error.invalidExtents\n                    }\n                    blockOffset += extentHeaderSize\n                    for _ in 0..<leafHeader.entries {\n                        let leaf = block.subdata(in: blockOffset..<blockOffset + extentLeafSize).withUnsafeBytes {\n                            $0.loadLittleEndian(as: ExtentLeaf.self)\n                        }\n                        extents.append((leaf.startLow, leaf.startLow + UInt32(leaf.length)))\n                        blockOffset += extentLeafSize\n                    }\n                    offset += extentIndexSize\n                }\n            default:\n                throw Error.deepExtentsUnimplemented\n            }\n            return extents\n        }\n\n        // MARK: Internal functions\n        func getInode(number: UInt32) throws -> Inode {\n            if let inode = self.inodes[number] {\n                return inode\n            }\n\n            let inode = try readInode(number)\n            self.inodes[number] = inode\n            return inode\n        }\n\n        func getGroupDescriptor(_ number: UInt32) throws -> GroupDescriptor {\n            if let gd = self.groupDescriptors[number] {\n                return gd\n            }\n            let gd = try readGroupDescriptor(number)\n            self.groupDescriptors[number] = gd\n            return gd\n        }\n\n        func children(of number: EXT4.InodeNumber) throws -> [(String, InodeNumber)] {\n            try getDirTree(number)\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/EXT4+Types.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//  swiftlint:disable large_tuple\n\nimport Foundation\n\nextension EXT4 {\n    public struct SuperBlock {\n        public var inodesCount: UInt32 = 0\n        public var blocksCountLow: UInt32 = 0\n        public var rootBlocksCountLow: UInt32 = 0\n        public var freeBlocksCountLow: UInt32 = 0\n        public var freeInodesCount: UInt32 = 0\n        public var firstDataBlock: UInt32 = 0\n        public var logBlockSize: UInt32 = 0\n        public var logClusterSize: UInt32 = 0\n        public var blocksPerGroup: UInt32 = 0\n        public var clustersPerGroup: UInt32 = 0\n        public var inodesPerGroup: UInt32 = 0\n        public var mtime: UInt32 = 0\n        public var wtime: UInt32 = 0\n        public var mountCount: UInt16 = 0\n        public var maxMountCount: UInt16 = 0\n        public var magic: UInt16 = 0\n        public var state: UInt16 = 0\n        public var errors: UInt16 = 0\n        public var minorRevisionLevel: UInt16 = 0\n        public var lastCheck: UInt32 = 0\n        public var checkInterval: UInt32 = 0\n        public var creatorOS: UInt32 = 0\n        public var revisionLevel: UInt32 = 0\n        public var defaultReservedUid: UInt16 = 0\n        public var defaultReservedGid: UInt16 = 0\n        public var firstInode: UInt32 = 0\n        public var inodeSize: UInt16 = 0\n        public var blockGroupNr: UInt16 = 0\n        public var featureCompat: UInt32 = 0\n        public var featureIncompat: UInt32 = 0\n        public var featureRoCompat: UInt32 = 0\n        public var uuid:\n            (\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8\n            ) = (\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0\n            )\n        public var volumeName:\n            (\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8\n            ) = (\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0\n            )\n        public var lastMounted:\n            (\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8\n            ) = (\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0\n            )\n        public var algorithmUsageBitmap: UInt32 = 0\n        public var preallocBlocks: UInt8 = 0\n        public var preallocDirBlocks: UInt8 = 0\n        public var reservedGdtBlocks: UInt16 = 0\n        public var journalUUID:\n            (\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8\n            ) = (\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0\n            )\n        public var journalInum: UInt32 = 0\n        public var journalDev: UInt32 = 0\n        public var lastOrphan: UInt32 = 0\n        public var hashSeed: (UInt32, UInt32, UInt32, UInt32) = (0, 0, 0, 0)\n        public var defHashVersion: UInt8 = 0\n        public var journalBackupType: UInt8 = 0\n        public var descSize: UInt16 = UInt16(MemoryLayout<GroupDescriptor>.size)\n        public var defaultMountOpts: UInt32 = 0\n        public var firstMetaBg: UInt32 = 0\n        public var mkfsTime: UInt32 = 0\n        public var journalBlocks:\n            (\n                UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32,\n                UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32,\n                UInt32\n            ) = (\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0\n            )\n        public var blocksCountHigh: UInt32 = 0\n        public var rBlocksCountHigh: UInt32 = 0\n        public var freeBlocksCountHigh: UInt32 = 0\n        public var minExtraIsize: UInt16 = 0\n        public var wantExtraIsize: UInt16 = 0\n        public var flags: UInt32 = 0\n        public var raidStride: UInt16 = 0\n        public var mmpInterval: UInt16 = 0\n        public var mmpBlock: UInt64 = 0\n        public var raidStripeWidth: UInt32 = 0\n        public var logGroupsPerFlex: UInt8 = 0\n        public var checksumType: UInt8 = 0\n        public var reservedPad: UInt16 = 0\n        public var kbytesWritten: UInt64 = 0\n        public var snapshotInum: UInt32 = 0\n        public var snapshotID: UInt32 = 0\n        public var snapshotRBlocksCount: UInt64 = 0\n        public var snapshotList: UInt32 = 0\n        public var errorCount: UInt32 = 0\n        public var firstErrorTime: UInt32 = 0\n        public var firstErrorInode: UInt32 = 0\n        public var firstErrorBlock: UInt64 = 0\n        public var firstErrorFunc:\n            (\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8\n            ) = (\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0\n            )\n        public var firstErrorLine: UInt32 = 0\n        public var lastErrorTime: UInt32 = 0\n        public var lastErrorInode: UInt32 = 0\n        public var lastErrorLine: UInt32 = 0\n        public var lastErrorBlock: UInt64 = 0\n        public var lastErrorFunc:\n            (\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8\n            ) = (\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0\n            )\n        public var mountOpts:\n            (\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8\n            ) = (\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0\n            )\n        public var userQuotaInum: UInt32 = 0\n        public var groupQuotaInum: UInt32 = 0\n        public var overheadBlocks: UInt32 = 0\n        public var backupBgs: (UInt32, UInt32) = (0, 0)\n        public var encryptAlgos: (UInt8, UInt8, UInt8, UInt8) = (0, 0, 0, 0)\n        public var encryptPwSalt:\n            (\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8\n            ) = (\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0\n            )\n        public var lpfInode: UInt32 = 0\n        public var projectQuotaInum: UInt32 = 0\n        public var checksumSeed: UInt32 = 0\n        public var wtimeHigh: UInt8 = 0\n        public var mtimeHigh: UInt8 = 0\n        public var mkfsTimeHigh: UInt8 = 0\n        public var lastcheckHigh: UInt8 = 0\n        public var firstErrorTimeHigh: UInt8 = 0\n        public var lastErrorTimeHigh: UInt8 = 0\n        public var pad: (UInt8, UInt8) = (0, 0)\n        public var reserved:\n            (\n                UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32,\n                UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32,\n                UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32,\n                UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32,\n                UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32,\n                UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32,\n                UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32,\n                UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32,\n                UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32,\n                UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32,\n                UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32,\n                UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32\n            ) = (\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0\n            )\n        public var checksum: UInt32 = 0\n    }\n\n    struct CompatFeature {\n        let rawValue: UInt32\n\n        static let dirPrealloc = CompatFeature(rawValue: 0x1)\n        static let imagicInodes = CompatFeature(rawValue: 0x2)\n        static let hasJournal = CompatFeature(rawValue: 0x4)\n        static let extAttr = CompatFeature(rawValue: 0x8)\n        static let resizeInode = CompatFeature(rawValue: 0x10)\n        static let dirIndex = CompatFeature(rawValue: 0x20)\n        static let lazyBg = CompatFeature(rawValue: 0x40)\n        static let excludeInode = CompatFeature(rawValue: 0x80)\n        static let excludeBitmap = CompatFeature(rawValue: 0x100)\n        static let sparseSuper2 = CompatFeature(rawValue: 0x200)\n    }\n\n    struct IncompatFeature {\n        let rawValue: UInt32\n\n        static let compression = IncompatFeature(rawValue: 0x1)\n        static let filetype = IncompatFeature(rawValue: 0x2)\n        static let recover = IncompatFeature(rawValue: 0x4)\n        static let journalDev = IncompatFeature(rawValue: 0x8)\n        static let metaBg = IncompatFeature(rawValue: 0x10)\n        static let extents = IncompatFeature(rawValue: 0x40)\n        static let bit64 = IncompatFeature(rawValue: 0x80)\n        static let mmp = IncompatFeature(rawValue: 0x100)\n        static let flexBg = IncompatFeature(rawValue: 0x200)\n        static let eaInode = IncompatFeature(rawValue: 0x400)\n        static let dirdata = IncompatFeature(rawValue: 0x1000)\n        static let csumSeed = IncompatFeature(rawValue: 0x2000)\n        static let largedir = IncompatFeature(rawValue: 0x4000)\n        static let inlineData = IncompatFeature(rawValue: 0x8000)\n        static let encrypt = IncompatFeature(rawValue: 0x10000)\n    }\n\n    struct RoCompatFeature {\n        let rawValue: UInt32\n\n        static let sparseSuper = RoCompatFeature(rawValue: 0x1)\n        static let largeFile = RoCompatFeature(rawValue: 0x2)\n        static let btreeDir = RoCompatFeature(rawValue: 0x4)\n        static let hugeFile = RoCompatFeature(rawValue: 0x8)\n        static let gdtCsum = RoCompatFeature(rawValue: 0x10)\n        static let dirNlink = RoCompatFeature(rawValue: 0x20)\n        static let extraIsize = RoCompatFeature(rawValue: 0x40)\n        static let hasSnapshot = RoCompatFeature(rawValue: 0x80)\n        static let quota = RoCompatFeature(rawValue: 0x100)\n        static let bigalloc = RoCompatFeature(rawValue: 0x200)\n        static let metadataCsum = RoCompatFeature(rawValue: 0x400)\n        static let replica = RoCompatFeature(rawValue: 0x800)\n        static let readonly = RoCompatFeature(rawValue: 0x1000)\n        static let project = RoCompatFeature(rawValue: 0x2000)\n    }\n\n    struct BlockGroupFlag {\n        let rawValue: UInt16\n\n        static let inodeUninit = BlockGroupFlag(rawValue: 0x1)\n        static let blockUninit = BlockGroupFlag(rawValue: 0x2)\n        static let inodeZeroed = BlockGroupFlag(rawValue: 0x4)\n    }\n\n    struct GroupDescriptor {\n        let blockBitmapLow: UInt32\n        let inodeBitmapLow: UInt32\n        let inodeTableLow: UInt32\n        let freeBlocksCountLow: UInt16\n        let freeInodesCountLow: UInt16\n        let usedDirsCountLow: UInt16\n        let flags: UInt16\n        let excludeBitmapLow: UInt32\n        let blockBitmapCsumLow: UInt16\n        let inodeBitmapCsumLow: UInt16\n        let itableUnusedLow: UInt16\n        let checksum: UInt16\n    }\n\n    struct GroupDescriptor64 {\n        let groupDescriptor: GroupDescriptor\n        let blockBitmapHigh: UInt32\n        let inodeBitmapHigh: UInt32\n        let inodeTableHigh: UInt32\n        let freeBlocksCountHigh: UInt16\n        let freeInodesCountHigh: UInt16\n        let usedDirsCountHigh: UInt16\n        let itableUnusedHigh: UInt16\n        let excludeBitmapHigh: UInt32\n        let blockBitmapCsumHigh: UInt16\n        let inodeBitmapCsumHigh: UInt16\n        let reserved: UInt32\n    }\n\n    public struct FileModeFlag: Sendable {\n        let rawValue: UInt16\n\n        public static let S_IXOTH = FileModeFlag(rawValue: 0x1)\n        public static let S_IWOTH = FileModeFlag(rawValue: 0x2)\n        public static let S_IROTH = FileModeFlag(rawValue: 0x4)\n        public static let S_IXGRP = FileModeFlag(rawValue: 0x8)\n        public static let S_IWGRP = FileModeFlag(rawValue: 0x10)\n        public static let S_IRGRP = FileModeFlag(rawValue: 0x20)\n        public static let S_IXUSR = FileModeFlag(rawValue: 0x40)\n        public static let S_IWUSR = FileModeFlag(rawValue: 0x80)\n        public static let S_IRUSR = FileModeFlag(rawValue: 0x100)\n        public static let S_ISVTX = FileModeFlag(rawValue: 0x200)\n        public static let S_ISGID = FileModeFlag(rawValue: 0x400)\n        public static let S_ISUID = FileModeFlag(rawValue: 0x800)\n        public static let S_IFIFO = FileModeFlag(rawValue: 0x1000)\n        public static let S_IFCHR = FileModeFlag(rawValue: 0x2000)\n        public static let S_IFDIR = FileModeFlag(rawValue: 0x4000)\n        public static let S_IFBLK = FileModeFlag(rawValue: 0x6000)\n        public static let S_IFREG = FileModeFlag(rawValue: 0x8000)\n        public static let S_IFLNK = FileModeFlag(rawValue: 0xA000)\n        public static let S_IFSOCK = FileModeFlag(rawValue: 0xC000)\n\n        public static let TypeMask = FileModeFlag(rawValue: 0xF000)\n    }\n\n    public typealias InodeNumber = UInt32\n\n    public struct Inode {\n        public var mode: UInt16 = 0\n        public var uid: UInt16 = 0\n        public var sizeLow: UInt32 = 0\n        public var atime: UInt32 = 0\n        public var ctime: UInt32 = 0\n        public var mtime: UInt32 = 0\n        public var dtime: UInt32 = 0\n        public var gid: UInt16 = 0\n        public var linksCount: UInt16 = 0\n        public var blocksLow: UInt32 = 0\n        public var flags: UInt32 = 0\n        public var version: UInt32 = 0\n        public var block:\n            (\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8\n            ) = (\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n            )\n        public var generation: UInt32 = 0\n        public var xattrBlockLow: UInt32 = 0\n        public var sizeHigh: UInt32 = 0\n        public var obsoleteFragmentAddr: UInt32 = 0\n        public var blocksHigh: UInt16 = 0\n        public var xattrBlockHigh: UInt16 = 0\n        public var uidHigh: UInt16 = 0\n        public var gidHigh: UInt16 = 0\n        public var checksumLow: UInt16 = 0\n        public var reserved: UInt16 = 0\n        public var extraIsize: UInt16 = 0\n        public var checksumHigh: UInt16 = 0\n        public var ctimeExtra: UInt32 = 0\n        public var mtimeExtra: UInt32 = 0\n        public var atimeExtra: UInt32 = 0\n        public var crtime: UInt32 = 0\n        public var crtimeExtra: UInt32 = 0\n        public var versionHigh: UInt32 = 0\n        public var projid: UInt32 = 0  // Size until this point is 160 bytes\n        public var inlineXattrs:\n            (  // 96 bytes for extended attributes\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,\n                UInt8, UInt8, UInt8, UInt8, UInt8, UInt8\n            ) = (\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                0, 0, 0, 0, 0, 0\n            )\n\n        public static func Mode(_ mode: FileModeFlag, _ perm: UInt16) -> UInt16 {\n            mode.rawValue | perm\n        }\n    }\n\n    struct InodeFlag {\n        let rawValue: UInt32\n\n        static let secRm = InodeFlag(rawValue: 0x1)\n        static let unRm = InodeFlag(rawValue: 0x2)\n        static let compressed = InodeFlag(rawValue: 0x4)\n        static let sync = InodeFlag(rawValue: 0x8)\n        static let immutable = InodeFlag(rawValue: 0x10)\n        static let append = InodeFlag(rawValue: 0x20)\n        static let noDump = InodeFlag(rawValue: 0x40)\n        static let noAtime = InodeFlag(rawValue: 0x80)\n        static let dirtyCompressed = InodeFlag(rawValue: 0x100)\n        static let compressedClusters = InodeFlag(rawValue: 0x200)\n        static let noCompress = InodeFlag(rawValue: 0x400)\n        static let encrypted = InodeFlag(rawValue: 0x800)\n        static let hashedIndex = InodeFlag(rawValue: 0x1000)\n        static let magic = InodeFlag(rawValue: 0x2000)\n        static let journalData = InodeFlag(rawValue: 0x4000)\n        static let noTail = InodeFlag(rawValue: 0x8000)\n        static let dirSync = InodeFlag(rawValue: 0x10000)\n        static let topDir = InodeFlag(rawValue: 0x20000)\n        static let hugeFile = InodeFlag(rawValue: 0x40000)\n        static let extents = InodeFlag(rawValue: 0x80000)\n        static let eaInode = InodeFlag(rawValue: 0x200000)\n        static let eofBlocks = InodeFlag(rawValue: 0x400000)\n        static let snapfile = InodeFlag(rawValue: 0x0100_0000)\n        static let snapfileDeleted = InodeFlag(rawValue: 0x0400_0000)\n        static let snapfileShrunk = InodeFlag(rawValue: 0x0800_0000)\n        static let inlineData = InodeFlag(rawValue: 0x1000_0000)\n        static let projectIDInherit = InodeFlag(rawValue: 0x2000_0000)\n        static let reserved = InodeFlag(rawValue: 0x8000_0000)\n    }\n\n    struct ExtentHeader {\n        let magic: UInt16\n        let entries: UInt16\n        let max: UInt16\n        let depth: UInt16\n        let generation: UInt32\n    }\n\n    struct ExtentIndex {\n        let block: UInt32\n        let leafLow: UInt32\n        let leafHigh: UInt16\n        let unused: UInt16\n    }\n\n    struct ExtentLeaf {\n        let block: UInt32\n        let length: UInt16\n        let startHigh: UInt16\n        let startLow: UInt32\n    }\n\n    struct ExtentTail {\n        let checksum: UInt32\n    }\n\n    struct ExtentIndexNode {\n        var header: ExtentHeader\n        var indices: [ExtentIndex]\n    }\n\n    struct ExtentLeafNode {\n        var header: ExtentHeader\n        var leaves: [ExtentLeaf]\n    }\n\n    struct DirectoryEntry {\n        let inode: InodeNumber\n        let recordLength: UInt16\n        let nameLength: UInt8\n        let fileType: UInt8\n        // let name: [UInt8]\n    }\n\n    enum FileType: UInt8 {\n        case unknown = 0x0\n        case regular = 0x1\n        case directory = 0x2\n        case character = 0x3\n        case block = 0x4\n        case fifo = 0x5\n        case socket = 0x6\n        case symbolicLink = 0x7\n    }\n\n    struct DirectoryEntryTail {\n        let reservedZero1: UInt32\n        let recordLength: UInt16\n        let reservedZero2: UInt8\n        let fileType: UInt8\n        let checksum: UInt32\n    }\n\n    struct DirectoryTreeRoot {\n        let dot: DirectoryEntry\n        let dotName: [UInt8]\n        let dotDot: DirectoryEntry\n        let dotDotName: [UInt8]\n        let reservedZero: UInt32\n        let hashVersion: UInt8\n        let infoLength: UInt8\n        let indirectLevels: UInt8\n        let unusedFlags: UInt8\n        let limit: UInt16\n        let count: UInt16\n        let block: UInt32\n        // let entries: [DirectoryTreeEntry]\n    }\n\n    struct DirectoryTreeNode {\n        let fakeInode: UInt32\n        let fakeRecordLength: UInt16\n        let nameLength: UInt8\n        let fileType: UInt8\n        let limit: UInt16\n        let count: UInt16\n        let block: UInt32\n        // let entries: [DirectoryTreeEntry]\n    }\n\n    struct DirectoryTreeEntry {\n        let hash: UInt32\n        let block: UInt32\n    }\n\n    struct DirectoryTreeTail {\n        let reserved: UInt32\n        let checksum: UInt32\n    }\n\n    struct XAttrEntry {\n        let nameLength: UInt8\n        let nameIndex: UInt8\n        let valueOffset: UInt16\n        let valueInum: UInt32\n        let valueSize: UInt32\n        let hash: UInt32\n    }\n\n    struct XAttrHeader {\n        let magic: UInt32\n        let referenceCount: UInt32\n        let blocks: UInt32\n        let hash: UInt32\n        let checksum: UInt32\n        let reserved: [UInt32]\n    }\n\n}\n\nextension EXT4.Inode {\n    public static func Root() -> EXT4.Inode {\n        var inode = Self()  // inode\n        inode.mode = Self.Mode(.S_IFDIR, 0o755)\n        inode.linksCount = 2\n        inode.uid = 0\n        inode.gid = 0\n        // time\n        let now = Date().fs()\n        let now_lo: UInt32 = now.lo\n        let now_hi: UInt32 = now.hi\n        inode.atime = now_lo\n        inode.atimeExtra = now_hi\n        inode.ctime = now_lo\n        inode.ctimeExtra = now_hi\n        inode.mtime = now_lo\n        inode.mtimeExtra = now_hi\n        inode.crtime = now_lo\n        inode.crtimeExtra = now_hi\n        inode.flags = EXT4.InodeFlag.hugeFile.rawValue\n        inode.extraIsize = UInt16(EXT4.ExtraIsize)\n        return inode\n    }\n\n}\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/EXT4+Xattrs.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/*\n * Note: Both the entries and values for the attributes need to occupy a size that is a multiple of 4,\n * meaning, in cases where the attribute name or value is less than not a multiple of 4, it is padded with 0\n * until it reaches that size.\n */\n\nextension EXT4 {\n    public struct ExtendedAttribute {\n        public static let prefixMap: [Int: String] = [\n            1: \"user.\",\n            2: \"system.posix_acl_access\",\n            3: \"system.posix_acl_default\",\n            4: \"trusted.\",\n            6: \"security.\",\n            7: \"system.\",\n            8: \"system.richacl\",\n        ]\n\n        let name: String\n        let index: UInt8\n        let value: [UInt8]\n\n        var sizeValue: UInt32 {\n            UInt32((value.count + 3) & ~3)\n        }\n\n        var sizeEntry: UInt32 {\n            UInt32((name.count + 3) & ~3 + 16)  // 16 bytes are needed to store other metadata for the xattr entry\n        }\n\n        var size: UInt32 {\n            sizeEntry + sizeValue\n        }\n\n        var fullName: String {\n            Self.decompressName(id: Int(index), suffix: name)\n        }\n\n        var hash: UInt32 {\n            var hash: UInt32 = 0\n            for char in name {\n                hash = (hash << 5) ^ (hash >> 27) ^ UInt32(char.asciiValue!)\n            }\n            var i = 0\n            while i + 3 < value.count {\n                let s = value[i..<i + 4]\n                let v = UInt32(littleEndian: s.withUnsafeBytes { $0.load(as: UInt32.self) })\n                hash = (hash << 16) ^ (hash >> 16) ^ v\n                i += 4\n            }\n            if value.count % 4 != 0 {\n                let last = value.count & ~3\n                var buff: [UInt8] = [0, 0, 0, 0]\n                for (i, byte) in value[last...].enumerated() {\n                    buff[i] = byte\n                }\n                let v = UInt32(littleEndian: buff.withUnsafeBytes { $0.load(as: UInt32.self) })\n                hash = (hash << 16) ^ (hash >> 16) ^ v\n            }\n            return hash\n        }\n\n        init(name: String, value: [UInt8]) {\n            let compressed = Self.compressName(name)\n            self.name = compressed.str\n            self.index = compressed.id\n            self.value = value\n        }\n\n        init(idx: UInt8, compressedName name: String, value: [UInt8]) {\n            self.name = name\n            self.index = idx\n            self.value = value\n        }\n\n        // MARK: Class methods\n        public static func compressName(_ name: String) -> (id: UInt8, str: String) {\n            for (id, prefix) in prefixMap.sorted(by: { $1.1.count < $0.1.count }) where name.hasPrefix(prefix) {\n                return (UInt8(id), String(name.dropFirst(prefix.count)))\n            }\n            return (0, name)\n        }\n\n        public static func decompressName(id: Int, suffix: String) -> String {\n            guard let prefix = prefixMap[id] else {\n                return suffix\n            }\n            return \"\\(prefix)\\(suffix)\"\n        }\n    }\n\n    public struct FileXattrsState {\n        private let inodeCapacity: UInt32\n        private let blockCapacity: UInt32\n        private let inode: UInt32  // the inode number for which we are tracking these xattrs\n\n        var inlineAttributes: [ExtendedAttribute] = []\n        var blockAttributes: [ExtendedAttribute] = []\n        private var usedSizeInline: UInt32 = 0\n        private var usedSizeBlock: UInt32 = 0\n\n        private var inodeFreeBytes: UInt32 {\n            self.inodeCapacity - EXT4.XattrInodeHeaderSize - usedSizeInline - 4  // need to have 4 null bytes b/w xattr entries and values\n        }\n\n        private var blockFreeBytes: UInt32 {\n            self.blockCapacity - EXT4.XattrBlockHeaderSize - usedSizeBlock - 4\n        }\n\n        init(inode: UInt32, inodeXattrCapacity: UInt32, blockCapacity: UInt32) {\n            self.inode = inode\n            self.inodeCapacity = inodeXattrCapacity\n            self.blockCapacity = blockCapacity\n        }\n\n        public mutating func add(_ attribute: ExtendedAttribute) throws {\n            let size = attribute.size\n            if size <= inodeFreeBytes {\n                usedSizeInline += size\n                inlineAttributes.append(attribute)\n                return\n            }\n            if size <= blockFreeBytes {\n                usedSizeBlock += size\n                blockAttributes.append(attribute)\n                return\n            }\n            throw Error.insufficientSpace(Int(self.inode))\n        }\n\n        public func writeInlineAttributes(buffer: inout [UInt8]) throws {\n            var idx = 0\n            withUnsafeLittleEndianBytes(\n                of: EXT4.XAttrHeaderMagic,\n                body: { bytes in\n                    for byte in bytes {\n                        buffer[idx] = byte\n                        idx += 1\n                    }\n                })\n            try Self.write(buffer: &buffer, attrs: self.inlineAttributes, start: UInt16(idx), delta: 0, inline: true)\n        }\n\n        public func writeBlockAttributes(buffer: inout [UInt8]) throws {\n            var idx = 0\n            for val in [EXT4.XAttrHeaderMagic, 1, 1] {\n                withUnsafeLittleEndianBytes(\n                    of: UInt32(val),\n                    body: { bytes in\n                        for byte in bytes {\n                            buffer[idx] = byte\n                            idx += 1\n                        }\n                    })\n            }\n            while idx != 32 {\n                buffer[idx] = 0\n                idx += 1\n            }\n            var attributes = self.blockAttributes\n            attributes.sort(by: {\n                if ($0.index < $1.index) || ($0.name.count < $1.name.count) || ($0.name < $1.name) {\n                    return true\n                }\n                return false\n            })\n            try Self.write(buffer: &buffer, attrs: attributes, start: UInt16(idx), delta: UInt16(idx), inline: false)\n        }\n\n        /// Writes the specified list of extended attribute entries and their values to the provided\n        /// This method does not fill in any headers (Inode inline / block level) that may be required to parse these attributes\n        ///\n        /// - Parameters:\n        ///   - buffer: An array of [UInt8] where the data will be written into\n        ///   - attrs: The list of ExtendedAttributes to write\n        ///   - start: the index from where data should be put into the buffer - useful when if you dont want this method to be overwriting existing data\n        ///   - delta: index from where the begin the offset calculations\n        ///   - inline: if the byte buffer being written into is an inline data block for an inode: Determines the hash calculation\n        private static func write(\n            buffer: inout [UInt8], attrs: [ExtendedAttribute], start: UInt16, delta: UInt16, inline: Bool\n        ) throws {\n            var offset: UInt16 = UInt16(buffer.count) + delta - start\n            var front = Int(start)\n            var end = buffer.count\n\n            for attribute in attrs {\n                guard end - front >= 4 else {\n                    throw Error.malformedXattrBuffer\n                }\n\n                var out: [UInt8] = []\n                let v = attribute.sizeValue\n                offset -= UInt16(v)\n                out.append(UInt8(attribute.name.count))\n                out.append(attribute.index)\n                withUnsafeLittleEndianBytes(\n                    of: UInt16(offset),\n                    body: { bytes in\n                        out.append(contentsOf: bytes)\n                    })\n                out.append(contentsOf: [0, 0, 0, 0])  // these next four bytes indicate that the attr values are in the same block\n                withUnsafeLittleEndianBytes(\n                    of: UInt32(attribute.value.count),\n                    body: { bytes in\n                        out.append(contentsOf: bytes)\n                    })\n                if !inline {\n                    withUnsafeLittleEndianBytes(\n                        of: UInt32(attribute.hash),\n                        body: { bytes in\n                            out.append(contentsOf: bytes)\n                        })\n                } else {\n                    out.append(contentsOf: [0, 0, 0, 0])\n                }\n                guard let name = attribute.name.data(using: .ascii) else {\n                    throw Error.convertAsciiString(attribute.name)\n                }\n                out.append(contentsOf: [UInt8](name))\n                while out.count < Int(attribute.sizeEntry) {  // ensure that xattr entry size is a multiple of 4\n                    out.append(0)\n                }\n                for (i, byte) in out.enumerated() {\n                    buffer[front + i] = byte\n                }\n                front += out.count\n\n                end -= Int(attribute.sizeValue)\n                for (i, byte) in attribute.value.enumerated() {\n                    buffer[end + i] = byte\n                }\n            }\n        }\n\n        public static func read(buffer: [UInt8], start: Int, offset: Int) throws -> [ExtendedAttribute] {\n            var i = start\n            var attribs: [ExtendedAttribute] = []\n            // 16 is the size of 1 XAttrEntry\n            while i + 16 < buffer.count {\n                let attributeStart = i\n                let rawXattrEntry = Array(buffer[i..<i + 16])\n                let xattrEntry = try EXT4.XAttrEntry(using: rawXattrEntry)\n                i += 16\n                var endIndex = i + Int(xattrEntry.nameLength)\n                guard endIndex < buffer.count else {\n                    continue\n                }\n                let rawName = buffer[i..<endIndex]\n                let name = String(bytes: rawName, encoding: .ascii)!\n                let valueStart = Int(xattrEntry.valueOffset) + offset\n                let valueEnd = Int(xattrEntry.valueOffset) + Int(xattrEntry.valueSize) + offset\n                let value = [UInt8](buffer[valueStart..<valueEnd])\n                let xattr = ExtendedAttribute(idx: xattrEntry.nameIndex, compressedName: name, value: value)\n                attribs.append(xattr)\n                i = attributeStart + xattr.sizeEntry\n                // The next 4 bytes being null indicate that there are no more attributes to read\n                endIndex = i + 3\n                guard endIndex < buffer.count else {\n                    continue\n                }\n                if Array(buffer[i...i + 3]) == [0, 0, 0, 0] {\n                    break\n                }\n            }\n            return attribs\n        }\n\n        public enum Error: CustomStringConvertible, Swift.Error {\n            case insufficientSpace(_ inode: Int)\n            case malformedXattrBuffer\n            case convertAsciiString(_ s: String)\n            case missingXAttrHeader\n\n            public var description: String {\n                switch self {\n                case .insufficientSpace(let inode):\n                    return \"cannot fit xattr for inode \\(inode)\"\n                case .malformedXattrBuffer:\n                    return \"malformed extended attribute buffer\"\n                case .convertAsciiString(let s):\n                    return \"cannot convert string \\(s) to a list of ASCII characters\"\n                case .missingXAttrHeader:\n                    return \"missing header for extended attribute entry\"\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/EXT4.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOS\nimport Foundation\n\n/**\n ```\n# EXT4 Filesystem Layout\n\n The EXT4 filesystem divides the disk into an upfront metadata section followed by several logical groups known as block groups. The\n metadata section looks like this:\n\n    +--------------------------+\n    |    Boot Sector (1024)    |\n    +--------------------------+\n    |    Superblock (1024)     |\n    +--------------------------+\n    |      Empty (2048)        |\n    +--------------------------+\n    |                          |\n    | [Block Group Descriptors]|\n    |                          |\n    | - Free/used block bitmap |\n    | - Free/used inode bitmap |\n    | - Inode table pointer    |\n    | - Other metadata         |\n    |                          |\n    +--------------------------+\n\n ## Block Groups\n\n Each block group optionally stores a copy of the superblock and group descriptor table for disaster recovery.\n The rest of the block group comprises of data blocks. The size of each block group is dynamically decided\n while formatting, based on total amount of space available on the disk.\n\n    +--------------------------+\n    |      Block Group 0       |\n    |    +------------------+  |\n    |    |   Super Block    |  |\n    |    +------------------+  |\n    |    |   Group Desc.    |  |\n    |    +------------------+  |\n    |    |   Data Blocks    |  |\n    |    |                  |  |\n    |    +------------------+  |\n    +--------------------------+\n    |      Block Group 1       |\n    |    +------------------+  |\n    |    |   Super Block    |  |\n    |    +------------------+  |\n    |    |   Group Desc.    |  |\n    |    +------------------+  |\n    |    |   Data Blocks    |  |\n    |    |                  |  |\n    |    +------------------+  |\n    +--------------------------+\n    |           ...            |\n    +--------------------------+\n    |      Block Group N       |\n    |    +------------------+  |\n    |    |   Super Block    |  |\n    |    +------------------+  |\n    |    |   Group Desc.    |  |\n    |    +------------------+  |\n    |    |   Data Blocks    |  |\n    |    |                  |  |\n    |    +------------------+  |\n    +--------------------------+\n\n The descriptor for each block group contain the following information:\n\n - Block Bitmap\n - Inode Bitmap\n - Pointer to Inode Table\n - other metadata such as used block count, num. dirs etc.\n\n ### Block Bitmap\n\n A sequence of bits, where each bit represents a block in the block group.\n\n    1: In use block\n    0: Free block\n\n    +---------------+---------------+\n    |             Block             |\n    |            Bitmap             |\n    +---------------+---------------+\n    | 1   0   1   0   1   1   0   0 |\n    +---------------+---------------+\n    |   |   |   |   |   |   |   |\n    |   |   |   |   |   |   |   |\n    v   v   v   v   v   v   v   v\n    +---+---+---+---+---+---+---+---+\n    | B |   | B |   | B | B |   |   |\n    +---+---+---+---+---+---+---+---+\n\n Whenever a file is created, free data blocks are identified by using this table.\n When it is deleted, the corresponding data blocks are marked as free.\n\n ### Inode Bitmap\n\n A sequence of bits, where each bit represents a inode in the block group. Since\n inodes per group is a fixed number, this bitmap is made to be of sufficient length\n to accommodate that many inodes\n\n    1: In use inode\n    0: Free inode\n\n    +---------------+---------------+\n    |             Inode             |\n    |            Bitmap             |\n    +---------------+---------------+\n    | 1   0   1   0   1   1   0   0 |\n    +---------------+---------------+\n    |   |   |   |   |   |   |   |\n    |   |   |   |   |   |   |   |\n    v   v   v   v   v   v   v   v\n    +---+---+---+---+---+---+---+---+\n    | I |   | I |   | I | I |   |   |\n    +---+---+---+---+---+---+---+---+\n\n ## Inode table\n\n Inode table provides a mapping from Inode -> Data blocks. In this implementation, inode size is set to 256 bytes.\n Inode table uses extents to efficiently describe the mapping.\n\n    +-----------------------+\n    |      Inode Table      |\n    +-----------------------+\n    | Inode | Metadata      |\n    +-------+---------------+\n    |   1   | permissions   |\n    |       | size          |\n    |       | user ID       |\n    |       | group ID      |\n    |       | timestamps    |\n    |       | block         |\n    |       | blocks count  |\n    +-------+---------------+\n    |   2   | ...           |\n    +-------+---------------+\n    |  ...  | ...           |\n    +-------+---------------+\n\n The length of `block` field in the inode table is 60 bytes. This field contains an extent tree\n that holds information about ranges of blocks used by the file. For smaller files, the entire extent\n tree can be stored within this field.\n\n    +-----------------------+\n    |        Inode          |\n    +-----------------------+\n    | Metadata              |\n    +-----------------------+\n    | Extent Tree           |\n    | +-------------------+ |\n    | | Extent Leaf Node  | |\n    | +-------------------+ |\n    | | - Start Block     | |\n    | | - Block Count     | |\n    | | - ...             | |\n    | +-------------------+ |\n    +-----------------------+\n\n For larger files which span across multiple non-contiguous blocks, extent tree's root points to extent\n blocks, which in-turn point to the blocks used by the file\n\n    +-----------------------+\n    | Extent Tree           |\n    | +-------------------+ |\n    | | Extent Root       | |\n    | +-------------------+ |\n    | | - Pointers to     | |\n    | |   Extent Blocks   | |\n    | +-------------------+ |\n    +-----------------------+\n          |\n          v\n    +-----------------------+\n    |     Extent Block      |\n    +-----------------------+\n    | +-------------------+ |\n    | | Extent Leaf Node  | |\n    | +-------------------+ |\n    | | - Start Block     | |\n    | | - Block Count     | |\n    | | - ...             | |\n    | +-------------------+ |\n    | +-------------------+ |\n    | | Extent Leaf Node  | |\n    | +-------------------+ |\n    | | - Start Block     | |\n    | | - Block Count     | |\n    | | - ...             | |\n    | +-------------------+ |\n    +-----------------------+\n\n ## Directory entries\n\n The data blocks for directory inodes point to a list of directory entrees. Each entry\n consists of only a name and inode number. The name and inode number correspond to the\n name and inode number of the children of the directory\n\n    +-------------------------+\n    |     Directory Entry     |\n    +-------------------------+\n    | inode | rec_len | name  |\n    +-------------------------+\n    |   2   |    1    |  \".\"  |\n    +-------------------------+\n    |     Directory Entry     |\n    +-------------------------+\n    | inode | rec_len | name  |\n    +-------------------------+\n    |   1   |    2    | \"..\"  |\n    +-------------------------+\n    |     Directory Entry     |\n    +-------------------------+\n    | inode | rec_len | name  |\n    +-------------------------+\n    |  11   |   10    | lost& |\n    |       |         | found |\n    +-------------------------+\n\nMore details can be found here https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout\n\n```\n*/\n\n/// A type for interacting with ext4 file systems.\n///\n/// The `Ext4` class provides functionality to read the superblock of an existing ext4 block device\n/// and format a new block device with the ext4 file system.\n///\n/// Usage:\n/// - To read the superblock of an existing ext4 block device, create an instance of `Ext4` with the\n///   path to the block device\n/// - To format a new block device with ext4, create an instance of `Ext4.Formatter` with the path to the block\n///   device and call the `close()` method.\n///\n/// Example 1: Read an existing block device\n/// ```swift\n///  let blockDevice = URL(filePath: \"/dev/sdb\")\n///  // succeeds if a valid ext4 fs is found at path\n///  let ext4 = try Ext4(blockDevice: blockDevice)\n///  print(\"Block size: \\(ext4.blockSize)\")\n///  print(\"Total size: \\(ext4.size)\")\n///\n///  // Reading the superblock\n///  let superblock = ext4.superblock\n///  print(\"Superblock information:\")\n///  print(\"Total blocks: \\(superblock.blocksCountLow)\")\n/// ```\n///\n/// Example 2: Format a new block device (Refer [`EXT4.Formatter`](x-source-tag://EXT4.Formatter) for more info)\n/// ```swift\n///  let devicePath = URL(filePath: \"/dev/sdc\")\n///  let formatter = try EXT4.Formatter(devicePath, blockSize: 4096)\n///  try formatter.close()\n/// ```\npublic enum EXT4 {\n    public static let SuperBlockMagic: UInt16 = 0xef53\n\n    static let ExtentHeaderMagic: UInt16 = 0xf30a\n    static let XAttrHeaderMagic: UInt32 = 0xea02_0000\n\n    static let DefectiveBlockInode: InodeNumber = 1\n    static let RootInode: InodeNumber = 2\n    static let FirstInode: InodeNumber = 11\n    static let LostAndFoundInode: InodeNumber = 11\n\n    static let InodeActualSize: UInt32 = 160  // 160 bytes used by metadata\n    static let InodeExtraSize: UInt32 = 96  // 96 bytes for inline xattrs\n    static let InodeSize: UInt32 = UInt32(MemoryLayout<Inode>.size)  // 256 bytes. This is the max size of an inode\n    static let XattrInodeHeaderSize: UInt32 = 4\n    static let XattrBlockHeaderSize: UInt32 = 32\n    static let ExtraIsize: UInt16 = UInt16(InodeActualSize) - 128\n\n    static let MaxLinks: UInt32 = 65000\n    static let MaxBlocksPerExtent: UInt32 = 0x8000\n    static let MaxFileSize: UInt64 = 128.gib()\n    static let SuperBlockOffset: UInt64 = 1024\n}\n\nextension EXT4 {\n    // `EXT4` errors.\n    public enum Error: Swift.Error, CustomStringConvertible, Sendable, Equatable {\n        case notFound(_ path: String)\n        case couldNotReadSuperBlock(_ path: String, _ offset: UInt64, _ size: Int)\n        case invalidSuperBlock\n        case deepExtentsUnimplemented\n        case invalidExtents\n        case invalidXattrEntry\n        case couldNotReadBlock(_ block: UInt32)\n        case invalidPathEncoding(_ path: String)\n        case couldNotReadInode(_ inode: UInt32)\n        case couldNotReadGroup(_ group: UInt32)\n        public var description: String {\n            switch self {\n            case .notFound(let path):\n                return \"file at path \\(path) not found\"\n            case .couldNotReadSuperBlock(let path, let offset, let size):\n                return \"could not read \\(size) bytes of superblock from \\(path) at offset \\(offset)\"\n            case .invalidSuperBlock:\n                return \"not a valid EXT4 superblock\"\n            case .deepExtentsUnimplemented:\n                return \"deep extents are not supported\"\n            case .invalidExtents:\n                return \"extents invalid or corrupted\"\n            case .invalidXattrEntry:\n                return \"invalid extended attribute entry\"\n            case .couldNotReadBlock(let block):\n                return \"could not read block \\(block)\"\n            case .invalidPathEncoding(let path):\n                return \"path encoding for '\\(path)' is invalid, must be ascii or utf8\"\n            case .couldNotReadInode(let inode):\n                return \"could not read inode \\(inode)\"\n            case .couldNotReadGroup(let group):\n                return \"could not read group descriptor \\(group)\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/EXT4Reader+Export.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(macOS)\nimport ContainerizationArchive\nimport Foundation\nimport SystemPackage\n\nextension EXT4.EXT4Reader {\n    public func export(archive: FilePath) throws {\n        let config = ArchiveWriterConfiguration(\n            format: .paxRestricted, filter: .none, options: [Options.xattrformat(.schily)])\n        let writer = try ArchiveWriter(configuration: config)\n        try writer.open(file: archive.url)\n        var items = self.tree.root.pointee.children\n        let hardlinkedInodes = Set(self.hardlinks.values)\n        var hardlinkTargets: [EXT4.InodeNumber: FilePath] = [:]\n\n        while items.count > 0 {\n            let itemPtr = items.removeFirst()\n            let item = itemPtr.pointee\n            let inode = try self.getInode(number: item.inode)\n            let entry = WriteEntry()\n            let mode = inode.mode\n            let size: UInt64 = (UInt64(inode.sizeHigh) << 32) | UInt64(inode.sizeLow)\n            entry.permissions = mode\n            guard let path = item.path else {\n                continue\n            }\n            if hardlinkedInodes.contains(item.inode) {\n                hardlinkTargets[item.inode] = path\n            }\n            guard self.hardlinks[path] == nil else {\n                continue\n            }\n            var attributes: [EXT4.ExtendedAttribute] = []\n            let buffer: [UInt8] = EXT4.tupleToArray(inode.inlineXattrs)\n            if !buffer.allZeros {\n                try attributes.append(contentsOf: Self.readInlineExtendedAttributes(from: buffer))\n            }\n            if inode.xattrBlockLow != 0 {\n                let block = inode.xattrBlockLow\n                try self.seek(block: block)\n                guard let buffer = try self.handle.read(upToCount: Int(self.blockSize)) else {\n                    throw EXT4.Error.couldNotReadBlock(block)\n                }\n                try attributes.append(contentsOf: Self.readBlockExtendedAttributes(from: [UInt8](buffer)))\n            }\n\n            var xattrs: [String: Data] = [:]\n            for attribute in attributes {\n                guard attribute.fullName != \"system.data\" else {\n                    continue\n                }\n                xattrs[attribute.fullName] = Data(attribute.value)\n            }\n\n            let pathStr = path.description\n            entry.path = pathStr\n            entry.size = Int64(size)\n            entry.group = gid_t(inode.gid)\n            entry.owner = uid_t(inode.uid)\n            entry.creationDate = Date(fsTimestamp: UInt64((inode.ctimeExtra << 32) | inode.ctime))\n            entry.modificationDate = Date(fsTimestamp: UInt64((inode.mtimeExtra << 32) | inode.mtime))\n            entry.contentAccessDate = Date(fsTimestamp: UInt64((inode.atimeExtra << 32) | inode.atime))\n            entry.xattrs = xattrs\n\n            if mode.isDir() {\n                entry.fileType = .directory\n                for child in item.children {\n                    items.append(child)\n                }\n                if pathStr == \"\" {\n                    continue\n                }\n                try writer.writeEntry(entry: entry, data: nil)\n            } else if mode.isReg() {\n                entry.fileType = .regular\n                var data = Data()\n                var remaining: UInt64 = size\n                if let block = item.blocks {\n                    for dataBlock in block.start..<block.end {\n                        try self.seek(block: dataBlock)\n                        var count: UInt64\n                        if remaining > self.blockSize {\n                            count = self.blockSize\n                        } else {\n                            count = remaining\n                        }\n                        guard let dataBytes = try self.handle.read(upToCount: Int(count)) else {\n                            throw EXT4.Error.couldNotReadBlock(dataBlock)\n                        }\n                        data.append(dataBytes)\n                        remaining -= UInt64(dataBytes.count)\n                    }\n                }\n                if let additionalBlocks = item.additionalBlocks {\n                    for block in additionalBlocks {\n                        for dataBlock in block.start..<block.end {\n                            try self.seek(block: dataBlock)\n                            var count: UInt64\n                            if remaining > self.blockSize {\n                                count = self.blockSize\n                            } else {\n                                count = remaining\n                            }\n                            guard let dataBytes = try self.handle.read(upToCount: Int(count)) else {\n                                throw EXT4.Error.couldNotReadBlock(dataBlock)\n                            }\n                            data.append(dataBytes)\n                            remaining -= UInt64(dataBytes.count)\n                        }\n                    }\n                }\n                try writer.writeEntry(entry: entry, data: data)\n            } else if mode.isLink() {\n                entry.fileType = .symbolicLink\n                if size < 60 {\n                    let linkBytes = EXT4.tupleToArray(inode.block)\n                    entry.symlinkTarget = String(bytes: linkBytes, encoding: .utf8) ?? \"\"\n                } else {\n                    if let block = item.blocks {\n                        try self.seek(block: block.start)\n                        guard let linkBytes = try self.handle.read(upToCount: Int(size)) else {\n                            throw EXT4.Error.couldNotReadBlock(block.start)\n                        }\n                        entry.symlinkTarget = String(bytes: linkBytes, encoding: .utf8) ?? \"\"\n                    }\n                }\n                try writer.writeEntry(entry: entry, data: nil)\n            } else {  // do not process sockets, fifo, character and block devices\n                continue\n            }\n        }\n        for (path, number) in self.hardlinks {\n            guard let targetPath = hardlinkTargets[number] else {\n                continue\n            }\n            let inode = try self.getInode(number: number)\n            let entry = WriteEntry()\n            entry.path = path.description\n            entry.hardlink = targetPath.description\n            entry.permissions = inode.mode\n            entry.group = gid_t(inode.gid)\n            entry.owner = uid_t(inode.uid)\n            entry.creationDate = Date(fsTimestamp: UInt64((inode.ctimeExtra << 32) | inode.ctime))\n            entry.modificationDate = Date(fsTimestamp: UInt64((inode.mtimeExtra << 32) | inode.mtime))\n            entry.contentAccessDate = Date(fsTimestamp: UInt64((inode.atimeExtra << 32) | inode.atime))\n            try writer.writeEntry(entry: entry, data: nil)\n        }\n        try writer.finishEncoding()\n    }\n\n    @available(*, deprecated, renamed: \"readInlineExtendedAttributes(from:)\")\n    public static func readInlineExtenedAttributes(from buffer: [UInt8]) throws -> [EXT4.ExtendedAttribute] {\n        try readInlineExtendedAttributes(from: buffer)\n    }\n\n    public static func readInlineExtendedAttributes(from buffer: [UInt8]) throws -> [EXT4.ExtendedAttribute] {\n        let header = UInt32(littleEndian: buffer[0...4].withUnsafeBytes { $0.load(as: UInt32.self) })\n        if header != EXT4.XAttrHeaderMagic {\n            throw EXT4.FileXattrsState.Error.missingXAttrHeader\n        }\n        return try EXT4.FileXattrsState.read(buffer: buffer, start: 4, offset: 4)\n    }\n\n    @available(*, deprecated, renamed: \"readBlockExtendedAttributes(from:)\")\n    public static func readBlockExtenedAttributes(from buffer: [UInt8]) throws -> [EXT4.ExtendedAttribute] {\n        try readBlockExtendedAttributes(from: buffer)\n    }\n\n    public static func readBlockExtendedAttributes(from buffer: [UInt8]) throws -> [EXT4.ExtendedAttribute] {\n        let header = UInt32(littleEndian: buffer[0...4].withUnsafeBytes { $0.load(as: UInt32.self) })\n        if header != EXT4.XAttrHeaderMagic {\n            throw EXT4.FileXattrsState.Error.missingXAttrHeader\n        }\n\n        return try EXT4.FileXattrsState.read(buffer: [UInt8](buffer), start: 32, offset: 0)\n    }\n\n    func seek(block: UInt32) throws {\n        try self.handle.seek(toOffset: UInt64(block) * blockSize)\n    }\n}\n\nextension Date {\n    init(fsTimestamp: UInt64) {\n        if fsTimestamp == 0 {\n            self = Date.distantPast\n            return\n        }\n\n        let seconds = Int64(fsTimestamp & 0x3_ffff_ffff)\n        let nanoseconds = Double(fsTimestamp >> 34) / 1_000_000_000\n\n        self = Date(timeIntervalSince1970: Double(seconds) + nanoseconds)\n    }\n}\n#endif\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/EXT4Reader+IO.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport SystemPackage\n\nextension EXT4 {\n    public enum PathIOError: Swift.Error, CustomStringConvertible {\n        case notFound(String)\n        case notAFile(String)\n        case isDirectory(String)\n        case notADirectory(String)\n        case symlinkLoop(String)\n        case invalidPath(String)\n\n        public var description: String {\n            switch self {\n            case .notFound(let p): return \"no such file or directory: \\(p)\"\n            case .notAFile(let p): return \"not a regular file: \\(p)\"\n            case .isDirectory(let p): return \"is a directory: \\(p)\"\n            case .notADirectory(let p): return \"not a directory: \\(p)\"\n            case .symlinkLoop(let p): return \"symlink loop while resolving: \\(p)\"\n            case .invalidPath(let p): return \"invalid path: \\(p)\"\n            }\n        }\n    }\n}\n\n// MARK: - Public API\n\nextension EXT4.EXT4Reader {\n    /// Return true if a path exists (file or directory) in this ext4 device.\n    public func exists(_ path: FilePath, followSymlinks: Bool = true) -> Bool {\n        (try? resolvePath(path, followSymlinks: followSymlinks).inode) != nil\n    }\n\n    /// Get the total number of blocks in the filesystem\n    private var totalBlocks: UInt64 {\n        let lo = UInt64(_superBlock.blocksCountLow)\n        let hi = UInt64(_superBlock.blocksCountHigh)\n        return lo | (hi << 32)\n    }\n\n    /// Validate that a physical block address is within device bounds\n    private func validateBlockAddress(_ block: UInt32) throws {\n        guard UInt64(block) < totalBlocks else {\n            throw EXT4.PathIOError.invalidPath(\"block address \\(block) exceeds device bounds (\\(totalBlocks) blocks)\")\n        }\n    }\n\n    /// Metadata (inode + inode number) for a path.\n    public func stat(_ path: FilePath, followSymlinks: Bool = true) throws -> (inodeNumber: EXT4.InodeNumber, inode: EXT4.Inode) {\n        let resolved = try resolvePath(path, followSymlinks: followSymlinks)\n        return (resolved.inodeNum, try getInode(number: resolved.inodeNum))\n    }\n\n    /// List a directory's entries (names only). Does not include \".\" or \"..\".\n    public func listDirectory(_ path: FilePath) throws -> [String] {\n        let (inoNum, ino) = try stat(path)\n        guard ino.mode.isDir() else {\n            throw EXT4.PathIOError.notADirectory(path.description)\n        }\n        let children = try children(of: inoNum)\n        return\n            children\n            .map { $0.0 }\n            .filter { $0 != \".\" && $0 != \"..\" }\n            .sorted()\n    }\n\n    /// Read bytes from a regular file at `path` into `buffer`, starting at `offset`.\n    /// Returns the number of bytes written to `buffer` (may be less than `buffer.count` at EOF).\n    /// - Note: Semantics mirror `read(2)`: partial reads are possible.\n    @discardableResult\n    public func readFile(\n        at path: FilePath,\n        into buffer: UnsafeMutableRawBufferPointer,\n        offset: UInt64 = 0,\n        followSymlinks: Bool = true\n    ) throws -> Int {\n        let context = try prepareRead(path: path, offset: offset, followSymlinks: followSymlinks)\n        if buffer.count == 0 || context.maxReadable == 0 {\n            return 0\n        }\n\n        let want = min(UInt64(buffer.count), context.maxReadable)\n        return try performRead(\n            inodeNumber: context.inodeNumber,\n            start: context.start,\n            wantedBytes: want,\n            into: buffer\n        )\n    }\n\n    /// Read bytes from a regular file at `path` starting at `offset`.\n    /// If `count` is nil, reads to EOF. Returns exactly the requested bytes (or less at EOF).\n    public func readFile(\n        at path: FilePath,\n        offset: UInt64 = 0,\n        count: Int? = nil,\n        followSymlinks: Bool = true\n    ) throws -> Data {\n        let context = try prepareRead(path: path, offset: offset, followSymlinks: followSymlinks)\n        let want = count.map { min(UInt64($0), context.maxReadable) } ?? context.maxReadable\n        if want == 0 {\n            return Data()\n        }\n\n        var out = Data(count: Int(want))\n        let wrote = try out.withUnsafeMutableBytes {\n            try performRead(\n                inodeNumber: context.inodeNumber,\n                start: context.start,\n                wantedBytes: want,\n                into: $0\n            )\n        }\n        if wrote < Int(want) {\n            out.removeSubrange(wrote..<Int(want))\n        }\n        return out\n    }\n\n    private func prepareRead(\n        path: FilePath,\n        offset: UInt64,\n        followSymlinks: Bool\n    ) throws -> (inodeNumber: EXT4.InodeNumber, start: UInt64, maxReadable: UInt64) {\n        let (inoNum, inode) = try stat(path, followSymlinks: followSymlinks)\n\n        if inode.mode.isDir() {\n            throw EXT4.PathIOError.isDirectory(path.description)\n        }\n\n        if !inode.mode.isReg() {\n            throw EXT4.PathIOError.notAFile(path.description)\n        }\n\n        let fileSize: UInt64 = inodeFileSize(inode)\n        let start = min(offset, fileSize)\n        let maxReadable = fileSize - start\n        return (inodeNumber: inoNum, start: start, maxReadable: maxReadable)\n    }\n\n    private func performRead(\n        inodeNumber: EXT4.InodeNumber,\n        start: UInt64,\n        wantedBytes: UInt64,\n        into buffer: UnsafeMutableRawBufferPointer\n    ) throws -> Int {\n        if wantedBytes == 0 {\n            return 0\n        }\n\n        guard let extents = try self.getExtents(inode: inodeNumber), !extents.isEmpty else {\n            return 0\n        }\n\n        for (physStartBlk, physEndBlk) in extents {\n            try validateBlockAddress(physStartBlk)\n            if physEndBlk > physStartBlk {\n                try validateBlockAddress(physEndBlk - 1)\n            }\n        }\n\n        guard let base = buffer.baseAddress else {\n            return 0\n        }\n\n        let desiredBytes = Int(min(wantedBytes, UInt64(buffer.count)))\n        if desiredBytes == 0 {\n            return 0\n        }\n\n        let blockSizeBytes = self.blockSize\n        let reqStart = start\n        let reqEnd = start + UInt64(desiredBytes)\n        var logicalOffset: UInt64 = 0\n        var bytesWritten = 0\n\n        for (physStartBlk, physEndBlk) in extents {\n            let extentBytes = UInt64(physEndBlk - physStartBlk) * blockSizeBytes\n            let logicalEnd = logicalOffset + extentBytes\n\n            if logicalEnd <= reqStart {\n                logicalOffset = logicalEnd\n                continue\n            }\n            if logicalOffset >= reqEnd {\n                break\n            }\n\n            let overlapStart = max(logicalOffset, reqStart)\n            let overlapEnd = min(logicalEnd, reqEnd)\n            var remaining = overlapEnd - overlapStart\n            if remaining == 0 {\n                logicalOffset = logicalEnd\n                continue\n            }\n\n            let offsetIntoExtent = overlapStart - logicalOffset\n            let absoluteByteOffset = (UInt64(physStartBlk) * blockSizeBytes) + offsetIntoExtent\n\n            do {\n                try self.handle.seek(toOffset: absoluteByteOffset)\n            } catch {\n                if bytesWritten > 0 {\n                    return bytesWritten\n                }\n                throw EXT4.PathIOError.invalidPath(\"failed to seek to offset \\(absoluteByteOffset): \\(error)\")\n            }\n\n            while remaining > 0 && bytesWritten < desiredBytes {\n                let chunk = min(desiredBytes - bytesWritten, Int(min(remaining, UInt64(1 << 20))))\n                let dest = UnsafeMutableRawBufferPointer(\n                    start: base.advanced(by: bytesWritten),\n                    count: chunk\n                )\n\n                do {\n                    guard let data = try self.handle.read(upToCount: chunk) else {\n                        return bytesWritten\n                    }\n\n                    if data.count == 0 {\n                        return bytesWritten\n                    }\n\n                    // Copy the data to the destination buffer\n                    data.withUnsafeBytes { sourceBytes in\n                        dest.copyMemory(from: UnsafeRawBufferPointer(sourceBytes))\n                    }\n\n                    bytesWritten += data.count\n                    remaining -= UInt64(data.count)\n\n                    if data.count < chunk && remaining > 0 {\n                        return bytesWritten\n                    }\n                } catch {\n                    if bytesWritten > 0 {\n                        return bytesWritten\n                    }\n                    throw error\n                }\n            }\n\n            logicalOffset = logicalEnd\n            if bytesWritten >= desiredBytes {\n                break\n            }\n        }\n\n        return bytesWritten\n    }\n\n    // MARK: - Internals inside EXT4Reader\n    public struct ResolvedPath {\n        let inodeNum: EXT4.InodeNumber\n        let inode: EXT4.Inode\n    }\n\n    /// Resolve a path to an inode (optionally following symlinks).\n    /// Paths may be absolute (\"/...\") or relative (from \"/\").\n    public func resolvePath(_ path: FilePath, followSymlinks: Bool, maxSymlinks: Int = 40) throws -> ResolvedPath {\n        var components: [String] = normalize(path: path)\n        var current: EXT4.InodeNumber = EXT4.RootInode\n        var parentStack: [EXT4.InodeNumber] = []  // Track parent chain for proper \"..\" handling\n\n        var symlinkHops = 0\n        var visitedInodes = Set<EXT4.InodeNumber>()\n\n        // Process components one at a time to handle symlinks in the middle of paths\n        var componentIndex = 0\n\n        while componentIndex < components.count {\n            let name = components[componentIndex]\n\n            if name == \".\" {\n                componentIndex += 1\n                continue\n            }\n\n            if name == \"..\" {\n                // Handle parent directory traversal\n                if current == EXT4.RootInode {\n                    // At root, \"..\" points to itself\n                    componentIndex += 1\n                    continue\n                }\n\n                // Use parent stack if available\n                if !parentStack.isEmpty {\n                    current = parentStack.removeLast()\n                } else {\n                    // Fallback: look up \"..\" entry in filesystem\n                    let entries = try children(of: current)\n                    if let parent = entries.first(where: { $0.0 == \"..\" })?.1 {\n                        current = parent\n                    }\n                }\n                componentIndex += 1\n                continue\n            }\n\n            // Regular component: verify current is a directory and look up child\n            let currentInode = try getInode(number: current)\n            guard currentInode.mode.isDir() else {\n                throw EXT4.PathIOError.notADirectory(name)\n            }\n\n            let entries = try children(of: current)\n            guard let child = entries.first(where: { $0.0 == name }) else {\n                throw EXT4.PathIOError.notFound(name)\n            }\n\n            // Check if child is a symlink\n            let childInode = try getInode(number: child.1)\n            if childInode.mode.isLink() && followSymlinks {\n                // Check for symlink loop\n                if visitedInodes.contains(child.1) {\n                    throw EXT4.PathIOError.symlinkLoop(FilePath(components.joined(separator: \"/\")).description)\n                }\n                visitedInodes.insert(child.1)\n\n                // Enforce max symlink depth\n                symlinkHops += 1\n                if symlinkHops > maxSymlinks {\n                    throw EXT4.PathIOError.symlinkLoop(FilePath(components.joined(separator: \"/\")).description)\n                }\n\n                // Read symlink target\n                let linkBytes = try readFileFromInode(inodeNum: child.1)\n                guard let linkTarget = String(data: linkBytes, encoding: .utf8), !linkTarget.isEmpty else {\n                    throw EXT4.PathIOError.invalidPath(\"empty symlink target\")\n                }\n\n                // Parse symlink target into components\n                let targetComponents = normalize(path: FilePath(linkTarget))\n\n                // Replace current component with symlink target components and continue\n                if linkTarget.hasPrefix(\"/\") {\n                    // Absolute symlink: reset to root\n                    current = EXT4.RootInode\n                    parentStack = []\n                    // Replace the symlink component with target components + remaining path\n                    components = targetComponents + Array(components[(componentIndex + 1)...])\n                    componentIndex = 0  // Start from beginning with new path\n                } else {\n                    // Relative symlink: continue from current directory\n                    // Replace the symlink component with target components + remaining path\n                    components = Array(components[0..<componentIndex]) + targetComponents + Array(components[(componentIndex + 1)...])\n                    // Don't change componentIndex - continue from same position with expanded path\n                }\n            } else {\n                // Not a symlink or not following symlinks - descend into directory\n                parentStack.append(current)\n                current = child.1\n                componentIndex += 1\n            }\n        }\n\n        // All components processed - return final inode\n        let finalInode = try getInode(number: current)\n        return ResolvedPath(inodeNum: current, inode: finalInode)\n    }\n\n    /// Walk a sequence of path components from a starting inode with parent tracking.\n    /// Returns the final inode and updated parent stack.\n    private func walkWithParents(\n        current start: EXT4.InodeNumber,\n        components: [String],\n        parentStack initialStack: [EXT4.InodeNumber]\n    ) throws -> (EXT4.InodeNumber, [EXT4.InodeNumber]) {\n        var current = start\n        var parentStack = initialStack\n\n        if components.isEmpty { return (current, parentStack) }\n\n        for name in components {\n            if name == \".\" {\n                continue\n            }\n\n            if name == \"..\" {\n                // Handle parent directory traversal with proper tracking\n                if current == EXT4.RootInode {\n                    // At root, \"..\" points to itself (POSIX behavior)\n                    continue\n                }\n\n                // Use parent stack if available for accurate traversal\n                if !parentStack.isEmpty {\n                    current = parentStack.removeLast()\n                } else {\n                    // No parent tracking available - look up \"..\" entry in filesystem\n                    // This happens when we start traversal from a non-root inode\n                    let entries = try children(of: current)\n                    if let parent = entries.first(where: { $0.0 == \"..\" })?.1 {\n                        current = parent\n                    }\n                }\n                continue\n            }\n\n            // Regular component: verify current is a directory before traversing\n            let currentInode = try getInode(number: current)\n            guard currentInode.mode.isDir() else {\n                throw EXT4.PathIOError.notADirectory(name)\n            }\n\n            // Look up child in current directory\n            let entries = try children(of: current)\n            guard let child = entries.first(where: { $0.0 == name }) else {\n                throw EXT4.PathIOError.notFound(name)\n            }\n\n            // Push current to parent stack before descending\n            parentStack.append(current)\n            current = child.1\n        }\n\n        return (current, parentStack)\n    }\n\n    /// Walk a sequence of path components from a starting inode.\n    private func walk(current start: EXT4.InodeNumber, components: [String]) throws -> EXT4.InodeNumber {\n        let (result, _) = try walkWithParents(current: start, components: components, parentStack: [])\n        return result\n    }\n\n    /// Normalize a path into components, handling absolute and relative paths.\n    private func normalize(path: FilePath) -> [String] {\n        let s = path.description\n        let trimmed = s.hasPrefix(\"/\") ? String(s.dropFirst()) : s\n        if trimmed.isEmpty { return [] }\n        return trimmed.split(separator: \"/\").map(String.init)\n    }\n\n    /// Read entire file content of a regular file given an inode (used for symlink targets).\n    private func readFileFromInode(inodeNum: EXT4.InodeNumber) throws -> Data {\n        let ino = try getInode(number: inodeNum)\n        guard ino.mode.isReg() || ino.mode.isLink() else {\n            return Data()\n        }\n        let size = inodeFileSize(ino)\n        if size == 0 { return Data() }\n\n        // Handle fast symlinks (target stored directly in inode block field)\n        if ino.mode.isLink() && size < 60 {\n            // Extract target from inode block field\n            let blockData = withUnsafeBytes(of: ino.block) { Data($0) }\n            return blockData.prefix(Int(size))\n        }\n\n        return try readFileBytesFromExtents(inodeNum: inodeNum, offset: 0, count: size)\n    }\n\n    /// Low-level read using extents, with explicit offset & length (in bytes).\n    private func readFileBytesFromExtents(inodeNum: EXT4.InodeNumber, offset: UInt64, count: UInt64) throws -> Data {\n        guard let extents = try self.getExtents(inode: inodeNum), !extents.isEmpty else {\n            return Data()\n        }\n\n        // Validate all extent blocks are within device bounds\n        for (startBlk, endBlk) in extents {\n            try validateBlockAddress(startBlk)\n            if endBlk > startBlk {\n                try validateBlockAddress(endBlk - 1)\n            }\n        }\n\n        var out = Data(capacity: Int(count))\n        var logicalOffset: UInt64 = 0\n        var bytesReadSuccessfully: Int = 0\n        let reqStart = offset\n        let reqEnd = offset + count\n        let bs = self.blockSize\n\n        for (startBlk, endBlk) in extents {\n            let extentBytes = UInt64(endBlk - startBlk) * bs\n            let logicalEnd = logicalOffset + extentBytes\n            if logicalEnd <= reqStart {\n                logicalOffset = logicalEnd\n                continue\n            }\n            if logicalOffset >= reqEnd { break }\n\n            let ovlStart = max(logicalOffset, reqStart)\n            let ovlEnd = min(logicalEnd, reqEnd)\n            let ovlLen = ovlEnd - ovlStart\n            if ovlLen == 0 {\n                logicalOffset = logicalEnd\n                continue\n            }\n\n            let offsetIntoExtent = ovlStart - logicalOffset\n            let absByteOffset = UInt64(startBlk) * bs + offsetIntoExtent\n\n            do {\n                try self.handle.seek(toOffset: absByteOffset)\n            } catch {\n                if bytesReadSuccessfully > 0 {\n                    // Return partial data that was successfully read\n                    return out\n                }\n                throw EXT4.PathIOError.invalidPath(\"failed to seek to offset \\(absByteOffset): \\(error)\")\n            }\n\n            var left = ovlLen\n            while left > 0 {\n                let chunk = Int(min(left, 1 << 20))\n\n                do {\n                    guard let data = try self.handle.read(upToCount: chunk) else {\n                        let blk = UInt32(absByteOffset / bs)\n                        throw EXT4.Error.couldNotReadBlock(blk)\n                    }\n\n                    out.append(data)\n                    bytesReadSuccessfully += data.count\n                    left -= UInt64(data.count)\n\n                    if data.count < chunk && left > 0 {\n                        // Partial read - return what we have\n                        return out\n                    }\n                } catch {\n                    if bytesReadSuccessfully > 0 {\n                        // Return partial data on error\n                        return out\n                    }\n                    throw error\n                }\n            }\n            logicalOffset = logicalEnd\n            if out.count >= Int(count) { break }\n        }\n        if out.count > Int(count) { out.removeSubrange(Int(count)..<out.count) }\n        return out\n    }\n\n    /// Compute 64-bit file size from the inode fields (i_size).\n    /// ext4 stores low 32 bits in i_size_lo and the high 32 bits in i_size_high when 64-bit sizes are enabled.\n    private func inodeFileSize(_ inode: EXT4.Inode) -> UInt64 {\n        // The Containerization EXT4 Inode struct exposes mode and block fields; size fields\n        // are commonly named sizeLo/sizeHigh in this codebase.\n        // EXT4 supports 64-bit file sizes - always use both low and high parts.\n        let lo = UInt64(inode.sizeLow)\n        let hi = UInt64(inode.sizeHigh)\n        return lo | (hi << 32)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/FilePath+Extensions.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport SystemPackage\n\nextension FilePath {\n    public static let Separator: String = \"/\"\n\n    public var bytes: [UInt8] {\n        self.withCString { cstr in\n            var ptr = cstr\n            var rawBytes: [UInt8] = []\n            while UInt(bitPattern: ptr) != 0 {\n                if ptr.pointee == 0x00 { break }\n                rawBytes.append(UInt8(bitPattern: ptr.pointee))\n                ptr = ptr.successor()\n            }\n            return rawBytes\n        }\n    }\n\n    public var base: String {\n        self.lastComponent?.string ?? \"/\"\n    }\n\n    public var dir: FilePath {\n        self.removingLastComponent()\n    }\n\n    public var url: URL {\n        URL(fileURLWithPath: self.string)\n    }\n\n    public var items: [String] {\n        self.components.map { $0.string }\n    }\n\n    public init(_ url: URL) {\n        self.init(url.path(percentEncoded: false))\n    }\n\n    public init?(_ data: Data) {\n        let cstr: String? = data.withUnsafeBytes { (rbp: UnsafeRawBufferPointer) in\n            guard let baseAddress = rbp.baseAddress else {\n                return nil\n            }\n\n            let cString = baseAddress.bindMemory(to: CChar.self, capacity: data.count)\n            return String(cString: cString)\n        }\n\n        guard let cstr else {\n            return nil\n        }\n        self.init(cstr)\n    }\n\n    public func join(_ path: FilePath) -> FilePath {\n        self.pushing(path)\n    }\n\n    public func join(_ path: String) -> FilePath {\n        self.join(FilePath(path))\n    }\n\n    public func split() -> (dir: FilePath, base: String) {\n        (self.dir, self.base)\n    }\n\n    public func clean() -> FilePath {\n        self.lexicallyNormalized()\n    }\n\n    public static func rel(_ basepath: String, _ targpath: String) -> FilePath {\n        let base = FilePath(basepath)\n        let targ = FilePath(targpath)\n\n        if base == targ {\n            return \".\"\n        }\n\n        let baseComponents = base.items\n        let targComponents = targ.items\n\n        var commonPrefix = 0\n        while commonPrefix < min(baseComponents.count, targComponents.count)\n            && baseComponents[commonPrefix] == targComponents[commonPrefix]\n        {\n            commonPrefix += 1\n        }\n\n        let upCount = baseComponents.count - commonPrefix\n        let relComponents = Array(repeating: \"..\", count: upCount) + targComponents[commonPrefix...]\n\n        return FilePath(relComponents.joined(separator: Self.Separator))\n    }\n}\n\nextension FileHandle {\n    public convenience init?(forWritingTo path: FilePath) {\n        self.init(forWritingAtPath: path.description)\n    }\n\n    public convenience init?(forReadingAtPath path: FilePath) {\n        self.init(forReadingAtPath: path.description)\n    }\n\n    public convenience init?(forReadingFrom path: FilePath) {\n        self.init(forReadingAtPath: path.description)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/FileTimestamps.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\npublic struct FileTimestamps {\n    public var access: Date\n    public var modification: Date\n    public var creation: Date\n    public var now: Date\n\n    public var accessLo: UInt32 {\n        access.fs().lo\n    }\n\n    public var accessHi: UInt32 {\n        access.fs().hi\n    }\n\n    public var modificationLo: UInt32 {\n        modification.fs().lo\n    }\n\n    public var modificationHi: UInt32 {\n        modification.fs().hi\n    }\n\n    public var creationLo: UInt32 {\n        creation.fs().lo\n    }\n\n    public var creationHi: UInt32 {\n        creation.fs().hi\n    }\n\n    public var nowLo: UInt32 {\n        now.fs().lo\n    }\n\n    public var nowHi: UInt32 {\n        now.fs().hi\n    }\n\n    public init(access: Date?, modification: Date?, creation: Date?) {\n        now = Date()\n        self.access = access ?? now\n        self.modification = modification ?? now\n        self.creation = creation ?? now\n    }\n\n    public init() {\n        self.init(access: nil, modification: nil, creation: nil)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/Formatter+Unpack.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(macOS)\nimport ContainerizationArchive\nimport Foundation\nimport ContainerizationOS\nimport SystemPackage\nimport ContainerizationExtras\n\nprivate typealias Hardlinks = [FilePath: FilePath]\n\nextension EXT4.Formatter {\n    /// Unpack the provided archive on to the ext4 filesystem.\n    public func unpack(reader: ArchiveReader, progress: ProgressHandler? = nil) throws {\n        var hardlinks: Hardlinks = [:]\n        // Allocate a single 128KiB reusable buffer for all files to minimize allocations\n        // and reduce the number of read calls to libarchive.\n        let bufferSize = 128 * 1024\n        let reusableBuffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: bufferSize)\n        defer { reusableBuffer.deallocate() }\n\n        for (entry, streamReader) in reader.makeStreamingIterator() {\n            try Task.checkCancellation()\n            guard var pathEntry = entry.path else {\n                continue\n            }\n\n            defer {\n                // Count the number of entries\n                if let progress {\n                    Task {\n                        await progress([\n                            .addItems(1)\n                        ])\n                    }\n                }\n            }\n\n            pathEntry = preProcessPath(s: pathEntry)\n            let path = FilePath(pathEntry)\n\n            if path.base.hasPrefix(\".wh.\") {\n                if path.base == \".wh..wh..opq\" {  // whiteout directory\n                    try self.unlink(path: path.dir, directoryWhiteout: true)\n                    continue\n                }\n                let startIndex = path.base.index(path.base.startIndex, offsetBy: \".wh.\".count)\n                let filePath = String(path.base[startIndex...])\n                let dir: FilePath = path.dir\n                try self.unlink(path: dir.join(filePath))\n                continue\n            }\n\n            if let hardlink = entry.hardlink {\n                let hl = preProcessPath(s: hardlink)\n                hardlinks[path] = FilePath(hl)\n                continue\n            }\n            let ts = FileTimestamps(\n                access: entry.contentAccessDate, modification: entry.modificationDate, creation: entry.creationDate)\n            switch entry.fileType {\n            case .directory:\n                try self.create(\n                    path: path, mode: EXT4.Inode.Mode(.S_IFDIR, entry.permissions), ts: ts, uid: entry.owner,\n                    gid: entry.group,\n                    xattrs: entry.xattrs)\n            case .regular:\n                try self.create(\n                    path: path, mode: EXT4.Inode.Mode(.S_IFREG, entry.permissions), ts: ts, buf: streamReader,\n                    uid: entry.owner,\n                    gid: entry.group, xattrs: entry.xattrs, fileBuffer: reusableBuffer)\n\n                // Count the size of files\n                if let progress, let size = entry.size {\n                    Task {\n                        await progress([\n                            .addSize(Int64(size))\n                        ])\n                    }\n                }\n            case .symbolicLink:\n                var symlinkTarget: FilePath?\n                if let target = entry.symlinkTarget {\n                    symlinkTarget = FilePath(target)\n                }\n                try self.create(\n                    path: path, link: symlinkTarget, mode: EXT4.Inode.Mode(.S_IFLNK, entry.permissions), ts: ts,\n                    uid: entry.owner,\n                    gid: entry.group, xattrs: entry.xattrs)\n            default:\n                continue\n            }\n        }\n        guard hardlinks.acyclic else {\n            throw UnpackError.circularLinks\n        }\n        for (path, _) in hardlinks {\n            if let resolvedTarget = try hardlinks.resolve(path) {\n                try self.link(link: path, target: resolvedTarget)\n            }\n        }\n    }\n\n    /// Unpack an archive at the source URL on to the ext4 filesystem.\n    public func unpack(\n        source: URL,\n        format: ContainerizationArchive.Format = .paxRestricted,\n        compression: ContainerizationArchive.Filter = .gzip,\n        progress: ProgressHandler? = nil\n    ) throws {\n        let reader = try ArchiveReader(\n            format: format,\n            filter: compression,\n            file: source\n        )\n        try self.unpack(reader: reader, progress: progress)\n    }\n\n    private func preProcessPath(s: String) -> String {\n        var p = s\n        if p.hasPrefix(\"./\") {\n            p = String(p.dropFirst())\n        }\n        if !p.hasPrefix(\"/\") {\n            p = \"/\" + p\n        }\n        return p\n    }\n}\n\n/// Common errors for unpacking an archive onto an ext4 filesystem.\npublic enum UnpackError: Swift.Error, CustomStringConvertible, Sendable, Equatable {\n    /// The name is invalid.\n    case invalidName(_ name: String)\n    /// A circular link is found.\n    case circularLinks\n\n    /// The description of the error.\n    public var description: String {\n        switch self {\n        case .invalidName(let name):\n            return \"'\\(name)' is an invalid name\"\n        case .circularLinks:\n            return \"circular links found\"\n        }\n    }\n}\n\nextension Hardlinks {\n    fileprivate var acyclic: Bool {\n        for (_, target) in self {\n            var visited: Set<FilePath> = [target]\n            var next = target\n            while let item = self[next] {\n                if visited.contains(item) {\n                    return false\n                }\n                next = item\n                visited.insert(next)\n            }\n        }\n        return true\n    }\n\n    fileprivate func resolve(_ key: FilePath) throws -> FilePath? {\n        let target = self[key]\n        guard let target else {\n            return nil\n        }\n        var next = target\n        let visited: Set<FilePath> = [next]\n        while let item = self[next] {\n            if visited.contains(item) {\n                throw UnpackError.circularLinks\n            }\n            next = item\n        }\n        return next\n    }\n}\n#endif\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/Integer+Extensions.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\nextension UInt64 {\n    public var lo: UInt32 {\n        UInt32(self & 0xffff_ffff)\n    }\n\n    public var hi: UInt32 {\n        UInt32(self >> 32)\n    }\n\n    public static func - (lhs: Self, rhs: UInt32) -> UInt64 {\n        lhs - UInt64(rhs)\n    }\n\n    public static func % (lhs: Self, rhs: UInt32) -> UInt64 {\n        lhs % UInt64(rhs)\n    }\n\n    public static func / (lhs: Self, rhs: UInt32) -> UInt32 {\n        (lhs / UInt64(rhs)).lo\n    }\n\n    public static func * (lhs: Self, rhs: UInt32) -> UInt64 {\n        lhs * UInt64(rhs)\n    }\n\n    public static func * (lhs: Self, rhs: Int) -> UInt64 {\n        lhs * UInt64(rhs)\n    }\n}\n\nextension UInt32 {\n    public var lo: UInt16 {\n        UInt16(self & 0xffff)\n    }\n\n    public var hi: UInt16 {\n        UInt16(self >> 16)\n    }\n\n    public static func + (lhs: Self, rhs: Int.IntegerLiteralType) -> UInt32 {\n        lhs + UInt32(rhs)\n    }\n\n    public static func - (lhs: Self, rhs: Int.IntegerLiteralType) -> UInt32 {\n        lhs - UInt32(rhs)\n    }\n\n    public static func / (lhs: Self, rhs: Int.IntegerLiteralType) -> UInt32 {\n        lhs / UInt32(rhs)\n    }\n\n    public static func - (lhs: Self, rhs: UInt16) -> UInt32 {\n        lhs - UInt32(rhs)\n    }\n\n    public static func * (lhs: Self, rhs: Int.IntegerLiteralType) -> Int {\n        Int(lhs) * rhs\n    }\n}\n\nextension Int {\n    public static func + (lhs: Self, rhs: UInt32) -> Int {\n        lhs + Int(rhs)\n    }\n\n    public static func + (lhs: Self, rhs: UInt32) -> UInt32 {\n        UInt32(lhs) + rhs\n    }\n}\n\nextension UInt16 {\n    func isDir() -> Bool {\n        self & EXT4.FileModeFlag.TypeMask.rawValue == EXT4.FileModeFlag.S_IFDIR.rawValue\n    }\n\n    func isLink() -> Bool {\n        self & EXT4.FileModeFlag.TypeMask.rawValue == EXT4.FileModeFlag.S_IFLNK.rawValue\n    }\n\n    func isReg() -> Bool {\n        self & EXT4.FileModeFlag.TypeMask.rawValue == EXT4.FileModeFlag.S_IFREG.rawValue\n    }\n\n    func fileType() -> UInt8 {\n        typealias FMode = EXT4.FileModeFlag\n        typealias FileType = EXT4.FileType\n        switch self & FMode.TypeMask.rawValue {\n        case FMode.S_IFREG.rawValue:\n            return FileType.regular.rawValue\n        case FMode.S_IFDIR.rawValue:\n            return FileType.directory.rawValue\n        case FMode.S_IFCHR.rawValue:\n            return FileType.character.rawValue\n        case FMode.S_IFBLK.rawValue:\n            return FileType.block.rawValue\n        case FMode.S_IFIFO.rawValue:\n            return FileType.fifo.rawValue\n        case FMode.S_IFSOCK.rawValue:\n            return FileType.socket.rawValue\n        case FMode.S_IFLNK.rawValue:\n            return FileType.symbolicLink.rawValue\n        default:\n            return FileType.unknown.rawValue\n        }\n    }\n}\n\nextension [UInt8] {\n    var allZeros: Bool {\n        for num in self where num != 0 {\n            return false\n        }\n        return true\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/README.md",
    "content": "# ``ContainerizationEXT4``\n\n`ContainerizationEXT4` provides functionality to read the superblock of an existing ext4 block device and format a new block device with\nthe ext4 file system.\n"
  },
  {
    "path": "Sources/ContainerizationEXT4/UnsafeLittleEndianBytes.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n// takes a pointer and converts its contents to native endian bytes\npublic func withUnsafeLittleEndianBytes<T, Result>(of value: T, body: (UnsafeRawBufferPointer) throws -> Result)\n    rethrows -> Result\n{\n    switch Endian {\n    case .little:\n        return try withUnsafeBytes(of: value) { bytes in\n            try body(bytes)\n        }\n    case .big:\n        return try withUnsafeBytes(of: value) { buffer in\n            let reversedBuffer = Array(buffer.reversed())\n            return try reversedBuffer.withUnsafeBytes { buf in\n                try body(buf)\n            }\n        }\n    }\n}\n\npublic func withUnsafeLittleEndianBuffer<T>(\n    of value: UnsafeRawBufferPointer, body: (UnsafeRawBufferPointer) throws -> T\n) rethrows -> T {\n    switch Endian {\n    case .little:\n        return try body(value)\n    case .big:\n        let reversed = Array(value.reversed())\n        return try reversed.withUnsafeBytes { buf in\n            try body(buf)\n        }\n    }\n}\n\nextension UnsafeRawBufferPointer {\n    // loads littleEndian raw data, converts it native endian format and calls UnsafeRawBufferPointer.load\n    public func loadLittleEndian<T>(as type: T.Type) -> T {\n        switch Endian {\n        case .little:\n            return self.load(as: T.self)\n        case .big:\n            let buffer = Array(self.reversed())\n            return buffer.withUnsafeBytes { ptr in\n                ptr.load(as: T.self)\n            }\n        }\n    }\n}\n\npublic enum Endianness {\n    case little\n    case big\n}\n\n// returns current endianness\npublic var Endian: Endianness {\n    switch CFByteOrderGetCurrent() {\n    case CFByteOrder(CFByteOrderLittleEndian.rawValue):\n        return .little\n    case CFByteOrder(CFByteOrderBigEndian.rawValue):\n        return .big\n    default:\n        fatalError(\"impossible\")\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationError/ContainerizationError.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// The core error type for Containerization.\n///\n/// Most API surfaces for the core container/process/agent types will\n/// return a ContainerizationError.\npublic struct ContainerizationError: Swift.Error, Sendable {\n    /// A code describing the error encountered.\n    public var code: Code\n    /// A description of the error.\n    public var message: String\n    /// The original error which led to this error being thrown.\n    public var cause: (any Error)?\n\n    /// Creates a new error.\n    ///\n    /// - Parameters:\n    ///   - code: The error code.\n    ///   - message: A description of the error.\n    ///   - cause: The original error which led to this error being thrown.\n    public init(_ code: Code, message: String, cause: (any Error)? = nil) {\n        self.code = code\n        self.message = message\n        self.cause = cause\n    }\n\n    /// Creates a new error.\n    ///\n    /// - Parameters:\n    ///   - rawCode: The error code value as a String.\n    ///   - message: A description of the error.\n    ///   - cause: The original error which led to this error being thrown.\n    public init(_ rawCode: String, message: String, cause: (any Error)? = nil) {\n        self.code = Code(rawValue: rawCode)\n        self.message = message\n        self.cause = cause\n    }\n\n    /// Provides a unique hash of the error.\n    public func hash(into hasher: inout Hasher) {\n        hasher.combine(self.code)\n        hasher.combine(self.message)\n    }\n\n    /// Equality operator for the error. Uses the code and message.\n    public static func == (lhs: Self, rhs: Self) -> Bool {\n        lhs.code == rhs.code && lhs.message == rhs.message\n    }\n\n    /// Checks if the given error has the provided code.\n    public func isCode(_ code: Code) -> Bool {\n        self.code == code\n    }\n}\n\nextension ContainerizationError: CustomStringConvertible {\n    /// Description of the error.\n    public var description: String {\n        guard let cause = self.cause else {\n            return \"\\(self.code): \\\"\\(self.message)\\\"\"\n        }\n        return \"\\(self.code): \\\"\\(self.message)\\\" (cause: \\\"\\(cause)\\\")\"\n    }\n}\n\nextension ContainerizationError: LocalizedError {\n    /// A localized message describing what error occurred.\n    public var errorDescription: String? {\n        guard let cause = self.cause else {\n            return message\n        }\n        return \"\\(message) (cause: \\\"\\(cause)\\\")\"\n    }\n}\n\nextension ContainerizationError {\n    /// Codes for a `ContainerizationError`.\n    public struct Code: Sendable, Hashable {\n        private enum Value: Hashable, Sendable, CaseIterable {\n            case unknown\n            case invalidArgument\n            case internalError\n            case exists\n            case notFound\n            case cancelled\n            case invalidState\n            case empty\n            case timeout\n            case unsupported\n            case interrupted\n        }\n\n        private var value: Value\n        private init(_ value: Value) {\n            self.value = value\n        }\n\n        init(rawValue: String) {\n            let values = Value.allCases.reduce(into: [String: Value]()) {\n                $0[String(describing: $1)] = $1\n            }\n\n            let match = values[rawValue]\n            guard let match else {\n                fatalError(\"invalid code value \\(rawValue)\")\n            }\n            self.value = match\n        }\n\n        public static var unknown: Self {\n            Self(.unknown)\n        }\n\n        public static var invalidArgument: Self {\n            Self(.invalidArgument)\n        }\n\n        public static var internalError: Self {\n            Self(.internalError)\n        }\n\n        public static var exists: Self {\n            Self(.exists)\n        }\n\n        public static var notFound: Self {\n            Self(.notFound)\n        }\n\n        public static var cancelled: Self {\n            Self(.cancelled)\n        }\n\n        public static var invalidState: Self {\n            Self(.invalidState)\n        }\n\n        public static var empty: Self {\n            Self(.empty)\n        }\n\n        public static var timeout: Self {\n            Self(.timeout)\n        }\n\n        public static var unsupported: Self {\n            Self(.unsupported)\n        }\n\n        public static var interrupted: Self {\n            Self(.interrupted)\n        }\n    }\n}\n\nextension ContainerizationError.Code: CustomStringConvertible {\n    public var description: String {\n        String(describing: self.value)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/AddressAllocator.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// Conforming objects can allocate and free various address types.\npublic protocol AddressAllocator<AddressType>: Sendable {\n    associatedtype AddressType: Sendable\n\n    /// Allocate a new address.\n    func allocate() throws -> AddressType\n\n    /// Attempt to reserve a specific address.\n    func reserve(_ address: AddressType) throws\n\n    /// Free an allocated address.\n    func release(_ address: AddressType) throws\n\n    /// If no addresses are allocated, prevent future allocations and return true.\n    func disableAllocator() -> Bool\n}\n\n/// Errors that a type implementing AddressAllocator should throw.\npublic enum AllocatorError: Swift.Error, CustomStringConvertible, Equatable {\n    case allocatorDisabled\n    case allocatorFull\n    case alreadyAllocated(_ address: String)\n    case invalidAddress(_ index: String)\n    case invalidArgument(_ msg: String)\n    case invalidIndex(_ index: Int)\n    case notAllocated(_ address: String)\n    case rangeExceeded\n\n    public var description: String {\n        switch self {\n        case .allocatorDisabled:\n            return \"the allocator is shutting down\"\n        case .allocatorFull:\n            return \"no free indices are available for allocation\"\n        case .alreadyAllocated(let address):\n            return \"cannot choose already-allocated address \\(address)\"\n        case .invalidAddress(let address):\n            return \"cannot create index using address \\(address)\"\n        case .invalidArgument(let msg):\n            return \"invalid argument: \\(msg)\"\n        case .invalidIndex(let index):\n            return \"cannot create address using index \\(index)\"\n        case .notAllocated(let address):\n            return \"cannot free unallocated address \\(address)\"\n        case .rangeExceeded:\n            return \"cannot create allocator that overflows maximum address value\"\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/AddressError.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\npublic struct AddressError: Error, Equatable, Hashable, CustomStringConvertible {\n    public var description: String {\n        String(describing: self.base)\n    }\n\n    @usableFromInline\n    enum Base: Equatable, Hashable, Sendable {\n        case unableToParse\n        case invalidZoneIdentifier\n        case invalidIPv4Suffix\n        case multipleEllipsis\n        case invalidHexGroup\n        case malformedAddress\n        case incompleteAddress\n    }\n\n    @usableFromInline\n    let base: Base\n\n    @inlinable\n    init(_ base: Base) { self.base = base }\n\n    public static var unableToParse: Self {\n        Self(.unableToParse)\n    }\n\n    public static var invalidZoneIdentifier: Self {\n        Self(.invalidZoneIdentifier)\n    }\n\n    public static var invalidIPv4SuffixInIPv6Address: Self {\n        Self(.invalidIPv4Suffix)\n    }\n\n    public static var multipleEllipsis: Self {\n        Self(.multipleEllipsis)\n    }\n\n    public static var invalidHexGroup: Self {\n        Self(.invalidHexGroup)\n    }\n\n    public static var malformedAddress: Self {\n        Self(.malformedAddress)\n    }\n\n    public static var incompleteAddress: Self {\n        Self(.incompleteAddress)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/AsyncLock.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Logging\n\n/// `AsyncLock` provides a familiar locking API, with the main benefit being that it\n/// is safe to call async methods while holding the lock. This is primarily used in spots\n/// where an actor makes sense, but we may need to ensure we don't fall victim to actor\n/// reentrancy issues.\npublic actor AsyncLock {\n    private var busy = false\n    private var queue: ArraySlice<CheckedContinuation<(), Never>> = []\n    private var log: Logger?\n\n    public struct Context: Sendable {\n        fileprivate init() {}\n    }\n\n    public init(log: Logger? = nil) {\n        self.log = log\n    }\n\n    /// withLock provides a scoped locking API to run a function while holding the lock.\n    public func withLock<T: Sendable>(logMetadata: Logger.Metadata? = nil, _ body: @Sendable @escaping (Context) async throws -> T) async rethrows -> T {\n        log?.debug(\"acquiring lock\", metadata: logMetadata)\n        while self.busy {\n            await withCheckedContinuation { cc in\n                self.queue.append(cc)\n            }\n        }\n\n        self.busy = true\n\n        defer {\n            self.busy = false\n            if let next = self.queue.popFirst() {\n                next.resume(returning: ())\n            } else {\n                self.queue = []\n            }\n        }\n\n        log?.debug(\"holding lock\", metadata: logMetadata)\n        defer { log?.debug(\"releasing lock\", metadata: logMetadata) }\n        let context = Context()\n        return try await body(context)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/AsyncMutex.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// `AsyncMutex` provides a mutex that protects a piece of data, with the main benefit being that it\n/// is safe to call async methods while holding the lock. This is primarily used in spots\n/// where an actor makes sense, but we may need to ensure we don't fall victim to actor\n/// reentrancy issues.\npublic actor AsyncMutex<T: Sendable> {\n    private final class Box: @unchecked Sendable {\n        var value: T\n        init(_ value: T) {\n            self.value = value\n        }\n    }\n\n    private var busy = false\n    private var queue: ArraySlice<CheckedContinuation<(), Never>> = []\n    private let box: Box\n\n    public init(_ initialValue: T) {\n        self.box = Box(initialValue)\n    }\n\n    /// withLock provides a scoped locking API to run a function while holding the lock.\n    /// The protected value is passed to the closure for safe access.\n    public func withLock<R: Sendable>(_ body: @Sendable @escaping (inout T) async throws -> R) async rethrows -> R {\n        while self.busy {\n            await withCheckedContinuation { cc in\n                self.queue.append(cc)\n            }\n        }\n\n        self.busy = true\n\n        defer {\n            self.busy = false\n            if let next = self.queue.popFirst() {\n                next.resume(returning: ())\n            } else {\n                self.queue = []\n            }\n        }\n\n        return try await body(&self.box.value)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/CIDR.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// Describes an IPv4 or IPv6 CIDR address block.\n@frozen\npublic enum CIDR: CustomStringConvertible, Equatable, Sendable, Hashable {\n\n    case v4(IPv4Address, Prefix)\n    case v6(IPv6Address, Prefix)\n\n    /// Create a CIDR address block.\n    public init(_ cidr: String) throws {\n        if let cidrV4 = try? CIDRv4(cidr) {\n            self = .v4(cidrV4.address, cidrV4.prefix)\n        } else if let cidrV6 = try? CIDRv6(cidr) {\n            self = .v6(cidrV6.address, cidrV6.prefix)\n        } else {\n            throw Error.invalidCIDR(cidr: cidr)\n        }\n    }\n\n    /// Create a CIDR address from a member IP and a prefix length.\n    public init(_ address: IPAddress, prefix: Prefix) throws {\n        switch address {\n        case .v4(let addr):\n            guard prefix.length <= 32 else {\n                throw Error.invalidCIDR(cidr: \"\\(address)/\\(prefix)\")\n            }\n            self = .v4(addr, prefix)\n        case .v6(let addr):\n            guard prefix.length <= 128 else {\n                throw Error.invalidCIDR(cidr: \"\\(address)/\\(prefix)\")\n            }\n            self = .v6(addr, prefix)\n        }\n    }\n\n    /// Create the smallest CIDR block that includes the lower and upper bounds.\n    ///\n    /// For type-safe construction, prefer `v4Range(lower:upper:)` or `v6Range(lower:upper:)`.\n    public init(lower: IPAddress, upper: IPAddress) throws {\n        switch (lower, upper) {\n        case (.v4(let lowerAddr), .v4(let upperAddr)):\n            let cidr = try CIDRv4(lower: lowerAddr, upper: upperAddr)\n            self = .v4(cidr.address, cidr.prefix)\n        case (.v6(let lowerAddr), .v6(let upperAddr)):\n            let cidr = try CIDRv6(lower: lowerAddr, upper: upperAddr)\n            self = .v6(cidr.address, cidr.prefix)\n        default:\n            throw Error.invalidAddressRange(lower: lower.description, upper: upper.description)\n        }\n    }\n\n    /// The IP component of this CIDR address.\n    @inlinable\n    public var address: IPAddress {\n        switch self {\n        case .v4(let addr, _):\n            return .v4(addr)\n        case .v6(let addr, _):\n            return .v6(addr)\n        }\n    }\n\n    /// The prefix length of this CIDR address.\n    @inlinable\n    public var prefix: Prefix {\n        switch self {\n        case .v4(_, let prefix), .v6(_, let prefix):\n            return prefix\n        }\n    }\n\n    /// The lowest address in this CIDR block\n    @inlinable\n    public var lower: IPAddress {\n        switch self {\n        case (.v4(let addr, let prefix)):\n            return .v4(IPv4Address(addr.value & prefix.prefixMask32))\n        case (.v6(let addr, let prefix)):\n            return .v6(IPv6Address(addr.value & prefix.prefixMask128))\n        }\n    }\n\n    /// The highest address in this CIDR block (broadcast address).\n    @inlinable\n    public var upper: IPAddress {\n        switch self {\n        case .v4(let addr, let prefix):\n            return .v4(IPv4Address(addr.value | prefix.suffixMask32))\n        case .v6(let addr, let prefix):\n            return .v6(IPv6Address(addr.value | prefix.suffixMask128, zone: addr.zone))\n        }\n    }\n\n    /// Return true if the CIDR block contains the specified address.\n    ///\n    /// Compares network portion of the given IP address.\n    @inlinable\n    public func contains(_ ip: IPAddress) -> Bool {\n        switch (self, ip) {\n        case (.v4(let network, let prefix), .v4(let ip)):\n            return network.value == (ip.value & prefix.prefixMask32)\n        case (.v6(let network, let prefix), .v6(let ip)):\n            return (network.zone == ip.zone) && (network.value == (ip.value & prefix.prefixMask128))\n        default:\n            return false\n        }\n    }\n\n    /// Retrieve the text representation of the CIDR block.\n    public var description: String {\n        \"\\(address)/\\(prefix)\"\n    }\n}\n\nextension CIDR {\n    public enum Error: Swift.Error {\n        case invalidCIDR(cidr: String)\n        case invalidAddressRange(lower: String, upper: String)\n    }\n}\n\nextension CIDR: Codable {\n    public init(from decoder: Decoder) throws {\n        let container = try decoder.singleValueContainer()\n        let string = try container.decode(String.self)\n        try self.init(string)\n    }\n\n    public func encode(to encoder: Encoder) throws {\n        var container = encoder.singleValueContainer()\n        try container.encode(description)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/CIDRv4.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// Describes an IPv4 CIDR address block.\n@frozen\npublic struct CIDRv4: CustomStringConvertible, Equatable, Sendable, Hashable {\n    /// The IP component of this CIDR address.\n    public let address: IPv4Address\n\n    /// The prefix length of this CIDR address.\n    public let prefix: Prefix\n\n    /// Create a CIDR address block.\n    public init(_ cidr: String) throws {\n        let split = cidr.split(separator: \"/\")\n        guard split.count == 2 else {\n            throw CIDR.Error.invalidCIDR(cidr: cidr)\n        }\n        guard let prefixLength = UInt8(split[1]), let prefix = Prefix(length: prefixLength) else {\n            throw CIDR.Error.invalidCIDR(cidr: cidr)\n        }\n\n        let address = try IPv4Address(String(split[0]))\n        try self.init(address, prefix: prefix)\n    }\n\n    /// Create a CIDR address from a member IP and a prefix length.\n    public init(_ address: IPv4Address, prefix: Prefix) throws {\n        guard prefix.length <= 32 else {\n            throw CIDR.Error.invalidCIDR(cidr: \"\\(address)/\\(prefix)\")\n        }\n        self.address = address\n        self.prefix = prefix\n    }\n\n    /// Create the smallest IPv4 CIDR block that includes the lower and upper bounds.\n    ///\n    /// - Parameters:\n    ///   - lower: The lower bound IPv4 address\n    ///   - upper: The upper bound IPv4 address\n    /// - Returns: The smallest CIDR block containing both addresses\n    /// - Throws: If lower > upper\n    public init(lower: IPv4Address, upper: IPv4Address) throws {\n        guard lower.value <= upper.value else {\n            throw CIDR.Error.invalidAddressRange(lower: lower.description, upper: upper.description)\n        }\n\n        for length in 1...32 {\n            let prefixLength = Prefix(unchecked: UInt8(length))\n            let mask = prefixLength.prefixMask32\n            if (lower.value & mask) != (upper.value & mask) {\n                let prefix = Prefix(unchecked: UInt8(length - 1))\n                let networkAddr = IPv4Address(lower.value & prefix.prefixMask32)\n                try self.init(networkAddr, prefix: prefix)\n                return\n            }\n        }\n        // Same address - /32 block\n        let prefix = Prefix(unchecked: 32)\n        let networkAddr = IPv4Address(lower.value & prefix.prefixMask32)\n        try self.init(networkAddr, prefix: prefix)\n    }\n\n    /// The lowest address in this CIDR block\n    @inlinable\n    public var lower: IPv4Address {\n        IPv4Address(address.value & prefix.prefixMask32)\n    }\n\n    /// The highest address in this CIDR block (broadcast address).\n    @inlinable\n    public var upper: IPv4Address {\n        IPv4Address(address.value | prefix.suffixMask32)\n    }\n\n    /// Return true if the CIDR block contains the specified address.\n    ///\n    /// Compares network portion of the given IP address.\n    @inlinable\n    public func contains(_ ip: IPv4Address) -> Bool {\n        (address.value & prefix.prefixMask32) == (ip.value & prefix.prefixMask32)\n    }\n\n    /// Retrieve the text representation of the CIDR block.\n    public var description: String {\n        \"\\(address)/\\(prefix)\"\n    }\n}\n\nextension CIDRv4: Codable {\n    public init(from decoder: Decoder) throws {\n        let container = try decoder.singleValueContainer()\n        let string = try container.decode(String.self)\n        try self.init(string)\n    }\n\n    public func encode(to encoder: Encoder) throws {\n        var container = encoder.singleValueContainer()\n        try container.encode(description)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/CIDRv6.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// Describes an IPv4 or IPv6 CIDR address block.\n@frozen\npublic struct CIDRv6: CustomStringConvertible, Equatable, Sendable, Hashable {\n\n    /// The IP component of this CIDR address.\n    public let address: IPv6Address\n\n    /// The prefix length of this CIDR address.\n    public let prefix: Prefix\n\n    /// Create a CIDR address block.\n    public init(_ cidr: String) throws {\n        let split = cidr.split(separator: \"/\")\n        guard split.count == 2 else {\n            throw CIDR.Error.invalidCIDR(cidr: cidr)\n        }\n        guard let prefixLength = UInt8(split[1]), let prefix = Prefix(length: prefixLength) else {\n            throw CIDR.Error.invalidCIDR(cidr: cidr)\n        }\n\n        let address = try IPv6Address(String(split[0]))\n        try self.init(address, prefix: prefix)\n    }\n\n    /// Create a CIDR address from a member IP and a prefix length.\n    public init(_ address: IPv6Address, prefix: Prefix) throws {\n        guard prefix.length <= 128 else {\n            throw CIDR.Error.invalidCIDR(cidr: \"\\(address)/\\(prefix)\")\n        }\n        self.address = address\n        self.prefix = prefix\n    }\n\n    /// Create the smallest IPv6 CIDR block that includes the lower and upper bounds.\n    ///\n    /// - Parameters:\n    ///   - lower: The lower bound IPv6 address\n    ///   - upper: The upper bound IPv6 address\n    /// - Returns: The smallest CIDR block containing both addresses\n    /// - Throws: If lower > upper or zones don't match\n    public init(lower: IPv6Address, upper: IPv6Address) throws {\n        guard lower.value <= upper.value && lower.zone == upper.zone else {\n            throw CIDR.Error.invalidAddressRange(lower: lower.description, upper: upper.description)\n        }\n\n        for length in 1...128 {\n            let prefixLength = Prefix(unchecked: UInt8(length))\n            let mask = prefixLength.prefixMask128\n            if (lower.value & mask) != (upper.value & mask) {\n                let prefix = Prefix(unchecked: UInt8(length - 1))\n                let networkAddr = IPv6Address(lower.value & prefix.prefixMask128, zone: lower.zone)\n                try self.init(networkAddr, prefix: prefix)\n                return\n            }\n        }\n        // Same address - /128 block\n        let prefix = Prefix(unchecked: 128)\n        let networkAddr = IPv6Address(lower.value & prefix.prefixMask128, zone: lower.zone)\n        try self.init(networkAddr, prefix: prefix)\n    }\n\n    /// The lowest address in this CIDR block\n    @inlinable\n    public var lower: IPv6Address {\n        IPv6Address(address.value & prefix.prefixMask128)\n    }\n\n    /// The highest address in this CIDR block (broadcast address).\n    @inlinable\n    public var upper: IPv6Address {\n        IPv6Address(address.value | prefix.suffixMask128, zone: address.zone)\n    }\n\n    /// Return true if the CIDR block contains the specified address.\n    ///\n    /// Compares network portion of the given IP address.\n    @inlinable\n    public func contains(_ ip: IPv6Address) -> Bool {\n        (address.zone == ip.zone) && ((address.value & prefix.prefixMask128) == (ip.value & prefix.prefixMask128))\n    }\n\n    /// Retrieve the text representation of the CIDR block.\n    public var description: String {\n        \"\\(address)/\\(prefix)\"\n    }\n}\n\nextension CIDRv6: Codable {\n    public init(from decoder: Decoder) throws {\n        let container = try decoder.singleValueContainer()\n        let string = try container.decode(String.self)\n        try self.init(string)\n    }\n\n    public func encode(to encoder: Encoder) throws {\n        var container = encoder.singleValueContainer()\n        try container.encode(description)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/FileManager+Temporary.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\nextension FileManager {\n    /// Returns a unique temporary directory to use.\n    public func uniqueTemporaryDirectory(create: Bool = true) -> URL {\n        let tempDirectoryURL = temporaryDirectory\n        let uniqueDirectoryURL = tempDirectoryURL.appendingPathComponent(UUID().uuidString)\n        if create {\n            try? createDirectory(at: uniqueDirectoryURL, withIntermediateDirectories: true, attributes: nil)\n        }\n        return uniqueDirectoryURL\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/IPAddress.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// Represents an IP address that can be either IPv4 or IPv6.\n@frozen\npublic enum IPAddress: Sendable, Hashable, CustomStringConvertible, Equatable {\n    /// An IPv4 address\n    case v4(IPv4Address)\n\n    /// An IPv6 address\n    case v6(IPv6Address)\n\n    /// Parses an IP address string, automatically detecting IPv4 or IPv6 format.\n    ///\n    /// - Parameter string: IP address string to parse\n    /// - Returns: An `IPAddress` containing either an IPv4 or IPv6 address\n    /// - Throws: `AddressError.unableToParse` if invalid\n    public init(_ string: String) throws {\n        let utf8 = string.utf8\n        var hasColon = false\n        var hasDot = false\n\n        for byte in utf8 {\n            if byte == 58 {  // ASCII ':'\n                hasColon = true\n                break\n            }\n            if byte == 46 {  // ASCII '.'\n                hasDot = true\n            }\n        }\n\n        if hasColon {\n            let ipv6 = try IPv6Address.parse(string)\n            self = .v6(ipv6)\n        } else if hasDot {\n            let ipv4 = try IPv4Address(string)\n            self = .v4(ipv4)\n        } else {\n            throw AddressError.unableToParse\n        }\n    }\n\n    /// String representation of the IP address.\n    public var description: String {\n        switch self {\n        case .v4(let addr):\n            return addr.description\n        case .v6(let addr):\n            return addr.description\n        }\n    }\n\n    /// Returns `true` if this is an IPv4 address.\n    @inlinable\n    public var isV4: Bool {\n        if case .v4 = self {\n            return true\n        }\n        return false\n    }\n\n    /// Returns `true` if this is an IPv6 address.\n    @inlinable\n    public var isV6: Bool {\n        if case .v6 = self {\n            return true\n        }\n        return false\n    }\n\n    /// Returns the underlying IPv4 address if this is an IPv4 address, otherwise `nil`.\n    @inlinable\n    public var ipv4: IPv4Address? {\n        if case .v4(let addr) = self {\n            return addr\n        }\n        return nil\n    }\n\n    /// Returns the underlying IPv6 address if this is an IPv6 address, otherwise `nil`.\n    @inlinable\n    public var ipv6: IPv6Address? {\n        if case .v6(let addr) = self {\n            return addr\n        }\n        return nil\n    }\n\n    /// Returns `true` if this is a loopback address (127.0.0.0/8 or ::1).\n    @inlinable\n    public var isLoopback: Bool {\n        switch self {\n        case .v4(let addr):\n            return addr.isLoopback\n        case .v6(let addr):\n            return addr.isLoopback\n        }\n    }\n\n    /// Returns `true` if this is a multicast address.\n    @inlinable\n    public var isMulticast: Bool {\n        switch self {\n        case .v4(let addr):\n            return addr.isMulticast\n        case .v6(let addr):\n            return addr.isMulticast\n        }\n    }\n\n    /// Returns `true` if this is an unspecified address (0.0.0.0 or ::).\n    @inlinable\n    public var isUnspecified: Bool {\n        switch self {\n        case .v4(let addr):\n            return addr.isUnspecified\n        case .v6(let addr):\n            return addr.isUnspecified\n        }\n    }\n}\n\nextension IPAddress: Codable {\n    public init(from decoder: Decoder) throws {\n        let container = try decoder.singleValueContainer()\n        let string = try container.decode(String.self)\n        try self.init(string)\n    }\n\n    public func encode(to encoder: Encoder) throws {\n        var container = encoder.singleValueContainer()\n        try container.encode(description)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/IPv4Address.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n@frozen\npublic struct IPv4Address: Sendable, Hashable, CustomStringConvertible, Equatable, Comparable {\n    public let value: UInt32\n\n    /// Creates an IPv4Address from an unsigned integer.\n    ///\n    /// - Parameter string: The integer representation of the address.\n    @inlinable\n    public init(_ value: UInt32) {\n        self.value = value\n    }\n\n    /// Creates an IPv4Address from 4 bytes.\n    ///\n    /// - Parameters:\n    ///   - bytes: 4-byte array in network byte order representing the IPv4 address\n    /// - Throws: `AddressError.unableToParse` if the byte array length is not 4\n    @inlinable\n    public init(_ bytes: [UInt8]) throws {\n        guard bytes.count == 4 else {\n            throw AddressError.unableToParse\n        }\n        self.value =\n            (UInt32(bytes[0]) << 24)\n            | (UInt32(bytes[1]) << 16)\n            | (UInt32(bytes[2]) << 16)\n            | UInt32(bytes[3])\n    }\n\n    /// Creates an IPv4Address from a string representation.\n    ///\n    /// - Parameter string: The IPv4 address string in dotted decimal notation (e.g., \"192.168.1.1\")\n    /// - Throws: `AddressError.unableToParse` if the string is not a valid IPv4 address\n    @inlinable\n    public init(_ string: String) throws {\n        self.value = try Self.parse(string)\n    }\n\n    @inlinable\n    public var bytes: [UInt8] {\n        Self.bytes(value)\n    }\n\n    @usableFromInline\n    static func bytes(_ value: UInt32) -> [UInt8] {\n        var result = [UInt8](repeating: 0, count: 4)\n        result[0] = UInt8((value >> 24) & 0xff)\n        result[1] = UInt8((value >> 16) & 0xff)\n        result[2] = UInt8((value >> 8) & 0xff)\n        result[3] = UInt8(value & 0xff)\n        return result\n    }\n\n    // TODO: spans?\n    @available(macOS 26.0, *)\n    @usableFromInline\n    static func bytes(_ value: UInt32) -> InlineArray<4, UInt8> {\n        let result: InlineArray<4, UInt8> = [\n            UInt8((value >> 24) & 0xff),\n            UInt8((value >> 16) & 0xff),\n            UInt8((value >> 8) & 0xff),\n            UInt8(value & 0xff),\n        ]\n        return result\n    }\n\n    @inlinable\n    public var description: String {\n        \"\\(bytes[0]).\\(bytes[1]).\\(bytes[2]).\\(bytes[3])\"\n    }\n\n    /// Parses an IPv4 address string in dotted decimal notation into a UInt32 representation.\n    ///\n    /// ## Validation Rules\n    /// - Exactly 4 octets separated by dots\n    /// - Each octet must be 0-255\n    /// - No leading zeros (except for \"0\" itself)\n    /// - No whitespace characters\n    /// - Only digits and dots allowed\n    ///\n    /// ## Examples\n    /// ```swift\n    /// IPv4Address.parse(\"192.168.1.1\")    // Returns: 3232235777\n    /// IPv4Address.parse(\"127.0.0.1\")      // Returns: 2130706433\n    /// IPv4Address.parse(\"0.0.0.0\")        // Returns: 0\n    /// IPv4Address.parse(\"255.255.255.255\") // Returns: 4294967295\n    ///\n    /// // Invalid examples:\n    /// IPv4Address.parse(\"192.168.1\")       // Wrong number of octets\n    /// IPv4Address.parse(\"192.168.1.256\")   // Octet out of range\n    /// IPv4Address.parse(\"192.168.001.1\")   // Leading zeros\n    /// IPv4Address.parse(\" 192.168.1.1 \")   // Whitespace\n    /// ```\n    ///\n    /// - Parameter s: The IPv4 address string to parse\n    /// - Returns: The 32-bit representation of the IP address, or `nil` if parsing fails\n    /// - Note: The returned value is in network byte order (big-endian)\n    @usableFromInline\n    internal static func parse(_ s: String) throws -> UInt32 {\n        guard !s.isEmpty, s.count >= 7, s.count <= 15 else {\n            throw AddressError.unableToParse\n        }\n\n        // IP addresses should only contain ASCII digits and dots\n        let utf8 = s.utf8\n        for byte in utf8 {\n            // ASCII whitespace: space(32), tab(9), newline(10), return(13)\n            if byte == 32 || byte == 9 || byte == 10 || byte == 13 {\n                throw AddressError.unableToParse\n            }\n        }\n\n        // accumulator for the 32bit representation of the IPv4 address\n        var result: UInt32 = 0\n\n        // tracking octet count, max 4 allowed\n        var octetCount = 0\n        var currentOctet = 0\n\n        // number of digits in the string representation of the octet, max 3\n        var digitCount = 0\n\n        for byte in utf8 {\n            if byte == 46 {  // ASCII '.'\n                // Validate octet before processing\n                guard octetCount < 3, digitCount > 0, digitCount <= 3, currentOctet <= 255 else {\n                    throw AddressError.unableToParse\n                }\n\n                // Shift result and add current octet\n                result = (result << 8) | UInt32(currentOctet)\n\n                // Reset for next octet\n                octetCount += 1\n                currentOctet = 0\n                digitCount = 0\n\n            } else if byte >= 48 && byte <= 57 {  // ASCII '0'-'9'\n                let digit = Int(byte - 48)\n\n                digitCount += 1\n\n                // Check for invalid leading zeros: \"01\", \"001\", etc.\n                // Allow single \"0\" but reject multi-digit numbers starting with 0\n                if digitCount == 1 && digit == 0 {\n                    // First digit is 0 - this is only valid if it's the only digit\n                    currentOctet = 0\n                } else if digitCount > 1 && currentOctet == 0 {\n                    // We had a leading zero and now have more digits - invalid\n                    throw AddressError.unableToParse\n                } else {\n                    // Normal case: build the octet value\n                    currentOctet = currentOctet * 10 + digit\n                }\n\n                // Early termination if octet becomes too large\n                guard currentOctet <= 255, digitCount <= 3 else {\n                    throw AddressError.unableToParse\n                }\n\n            } else {\n                throw AddressError.unableToParse\n            }\n        }\n\n        // Validate final octet\n        guard octetCount == 3, digitCount > 0, digitCount <= 3, currentOctet <= 255 else {\n            throw AddressError.unableToParse\n        }\n\n        return (result << 8) | UInt32(currentOctet)\n    }\n\n    // MARK: - Address Classification Methods\n\n    /// Returns `true` if this is the unspecified address (0.0.0.0).\n    ///\n    /// Per RFC 791, 0.0.0.0 is the \"this network\" address.\n    @inlinable\n    public var isUnspecified: Bool {\n        value == 0\n    }\n\n    /// Returns `true` if this is a loopback address (127.0.0.0/8).\n    ///\n    /// Per RFC 1122 Section 3.2.1.3, the entire 127.0.0.0/8 block is reserved for loopback.\n    @inlinable\n    public var isLoopback: Bool {\n        (value & 0xFF00_0000) == 0x7F00_0000\n    }\n\n    /// Returns `true` if this is a multicast address (224.0.0.0/4).\n    ///\n    /// Per RFC 1112, addresses in the range 224.0.0.0 to 239.255.255.255 are multicast addresses.\n    @inlinable\n    public var isMulticast: Bool {\n        (value & 0xF000_0000) == 0xE000_0000\n    }\n\n    /// Returns `true` if this is a link-local address (169.254.0.0/16).\n    ///\n    /// Per RFC 3927, 169.254.0.0/16 is reserved for link-local addresses (APIPA/Auto-IP).\n    @inlinable\n    public var isLinkLocal: Bool {\n        (value & 0xFFFF_0000) == 0xA9FE_0000\n    }\n\n    /// Returns `true` if this is the limited broadcast address (255.255.255.255).\n    ///\n    /// Per RFC 919/922, 255.255.255.255 is the limited broadcast address.\n    @inlinable\n    public var isBroadcast: Bool {\n        value == 0xFFFF_FFFF\n    }\n\n    /// Compares two IPv4 addresses numerically.\n    @inlinable\n    public static func < (lhs: IPv4Address, rhs: IPv4Address) -> Bool {\n        lhs.value < rhs.value\n    }\n}\n\nextension IPv4Address: Codable {\n    public init(from decoder: Decoder) throws {\n        let container = try decoder.singleValueContainer()\n        let string = try container.decode(String.self)\n        try self.init(string)\n    }\n\n    public func encode(to encoder: Encoder) throws {\n        var container = encoder.singleValueContainer()\n        try container.encode(description)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/IPv6Address+Parse.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nextension IPv6Address {\n    /// Parses an IPv6 address string into an IPv6Address instance.\n    ///\n    /// Follows RFC 4291 and RFC 5952.\n    ///\n    /// This function supports standard IPv6 notation including:\n    /// - Full addresses: `2001:0db8:0000:0042:0000:8a2e:0370:7334`\n    /// - Zero compression: `2001:db8::8a2e:370:7334`\n    /// - Leading zero omission: `2001:db8:0:42:0:8a2e:370:7334`\n    /// - Unspecified address: `::`\n    /// - Zone identifiers: `fe80::1%eth0`\n    ///\n    /// - Parameter input: IPv6 address string (with optional zone identifier after %)\n    /// - Returns: An `IPv6Address` instance representing the parsed address\n    ///\n    /// ## Example Usage\n    /// ```swift\n    /// let addr1 = try IPv6Address.parse(\"2001:db8::1\")\n    /// let addr2 = try IPv6Address.parse(\"::\") // Unspecified address\n    /// let addr3 = try IPv6Address.parse(\"fe80::1%eth0\") // With zone identifier\n    /// ```\n    static func parse(_ input: String) throws -> IPv6Address {\n        var ipBytes = [UInt8](repeating: 0, count: 16)\n        var ellipsisPosition: Int?\n\n        // Extract zone identifier\n        let (address, zone) = try extractZoneIdentifier(from: input)\n\n        // RFC 4291 Section 2.2.3: IPv4 suffix must be at the end (last 32 bits)\n        var remainingAddress = address\n        var ipv6ByteLimit = 16  // Maximum bytes available for IPv6 hex groups\n        var hasIPv4Suffix = false\n\n        // check if the IPv6 address has IPv4 address in it.\n        if let (ipv6Part, ipv4Bytes) = try extractIPv4Suffix(from: address) {\n            // If IPv4 present, save directly to last 4 bytes.\n            ipBytes[12] = ipv4Bytes[0]\n            ipBytes[13] = ipv4Bytes[1]\n            ipBytes[14] = ipv4Bytes[2]\n            ipBytes[15] = ipv4Bytes[3]\n\n            // Update address and limit IPv6 parsing to first 12 bytes (6 groups max)\n            remainingAddress = ipv6Part\n            ipv6ByteLimit = 12\n            hasIPv4Suffix = true\n        }\n\n        // Handle leading ellipsis in the IPv6 part\n        if remainingAddress.utf8.starts(with: [58, 58]) {  // \"::\"\n            ellipsisPosition = 0\n            remainingAddress = String(remainingAddress.dropFirst(2))\n\n            // Special case: \"::\" represents the unspecified address (all zeros)\n            // But if we have IPv4 suffix, the IPv4 bytes are already set correctly\n            if remainingAddress.isEmpty {\n                // If we have IPv4 suffix, ipBytes already has the IPv4 data, just return\n                if hasIPv4Suffix {\n                    return try Self(ipBytes, zone: zone)\n                }\n\n                // Pure \"::\" - Return the unspecified address, handling zone identifiers\n                if let zone = zone, !zone.isEmpty {\n                    return try Self(ipBytes, zone: zone)\n                }\n                return .unspecified\n            }\n        }\n\n        // Parse IPv6 hex groups up to the byte limit\n        var byteIndex = 0\n        let utf8 = remainingAddress.utf8\n        var currentPosition = utf8.startIndex\n\n        while byteIndex < ipv6ByteLimit && currentPosition < utf8.endIndex {\n            let (hexValue, nextPosition) = try parseHexadecimal(\n                from: utf8,\n                startingAt: currentPosition\n            )\n\n            // Store the UInt16 in network-byte order\n            ipBytes[byteIndex] = UInt8(hexValue >> 8)\n            ipBytes[byteIndex + 1] = UInt8(hexValue & 0xFF)\n            byteIndex += 2\n            currentPosition = nextPosition\n\n            // Terminate early if we have consumed the whole string\n            if currentPosition == utf8.endIndex {\n                break\n            }\n\n            // Parse separator and handle ellipsis detection\n            currentPosition = try skipColonSeparator(\n                from: utf8,\n                at: currentPosition,\n                currentByteIndex: byteIndex,\n                ellipsisPosition: &ellipsisPosition\n            )\n        }\n\n        // Validate complete consumption of input\n        guard currentPosition >= utf8.endIndex else {\n            throw AddressError.malformedAddress\n        }\n\n        // Apply ellipsis expansion for the IPv6 portion\n        try expandEllipsis(\n            in: &ipBytes,\n            parsedBytes: byteIndex,\n            ellipsisPosition: ellipsisPosition,\n            byteLimit: ipv6ByteLimit\n        )\n        let value = ipBytes.reduce(UInt128(0)) { ($0 << 8) | UInt128($1) }\n        return Self(value, zone: zone)\n    }\n\n    // MARK: - Helper Functions\n\n    /// Extracts IPv4 suffix if present at the end of the address\n    ///\n    /// Follows: RFC 4291 Section 2.2.3: Alternative form x:x:x:x:x:x:d.d.d.d\n    /// IPv4 must be the last 32 bits and preceded by a colon\n    ///\n    /// - Parameter input: The IPv6 address string to check\n    /// - Returns: Optional tuple of (IPv6 part without IPv4, IPv4 bytes array) if IPv4 found, nil otherwise\n    /// - Throws: `AddressError.invalidIPv4Suffix` for invalid IPv4 addresses\n    internal static func extractIPv4Suffix(from input: String) throws -> (String, [UInt8])? {\n        // must contain a dot to be IPv4\n        guard input.utf8.contains(46) else {  // ASCII '.'\n            return nil\n        }\n\n        // IPv4 address must be present after last colon\n        guard let lastColonIndex = input.lastIndex(of: \":\") else {\n            return nil\n        }\n\n        // TODO: maybe refactor for performance\n        let afterColon = input.index(after: lastColonIndex)\n        guard afterColon < input.endIndex else {\n            return nil\n        }\n\n        let possibleIPv4 = String(input[afterColon...])\n        guard let ipv4Value = try? IPv4Address.parse(possibleIPv4) else {\n            throw AddressError.invalidIPv4SuffixInIPv6Address\n        }\n\n        // Check if lastColonIndex is the second ':' of '::'. If so, ensure to include it.\n        let isDoubleColon = lastColonIndex > input.startIndex && input[input.index(before: lastColonIndex)] == \":\"\n        let ipv6Part = isDoubleColon ? String(input[...lastColonIndex]) : String(input[..<lastColonIndex])\n        return (ipv6Part, IPv4Address.bytes(ipv4Value))\n    }\n\n    /// Extracts zone identifier\n    ///\n    /// - Parameter input: The full IPv6 address string with potential zone identifier\n    /// - Returns: Tuple of (address part, optional zone identifier)\n    /// - Throws: `AddressError.invalidZoneIdentifier` for malformed zone identifiers\n    private static func extractZoneIdentifier(from input: String) throws -> (String, String?) {\n        guard let percentIndex = input.lastIndex(of: \"%\") else {\n            return (input, nil)\n        }\n\n        let zoneStartIndex = input.index(after: percentIndex)\n        guard zoneStartIndex < input.endIndex else {\n            throw AddressError.invalidZoneIdentifier\n        }\n\n        let addressPart = String(input[..<percentIndex])\n        let zoneIdentifier = String(input[zoneStartIndex...])\n\n        return (addressPart, zoneIdentifier)\n    }\n\n    /// Parses a hexadecimal group from an IPv6 address component.\n    ///\n    /// - Parameters:\n    ///   - utf8: The UTF-8 view to parse from\n    ///   - startIndex: Starting position in the UTF-8 view\n    /// - Returns: Tuple of (parsed hex value as UInt16, next position after parsed digits)\n    /// - Throws: `AddressError.invalidHexGroup` if no valid hex digits are found\n    ///\n    /// ## Example\n    /// ```swift\n    /// let utf8 = \"2001:db8::1\".utf8\n    /// let (value, nextPos) = try parseHexadecimal(from: utf8, startingAt: utf8.startIndex)\n    /// // value = 0x2001, nextPos points to ':'\n    /// ```\n    @inlinable\n    internal static func parseHexadecimal(\n        from group: String.UTF8View,\n        startingAt startIndex: String.UTF8View.Index\n    ) throws -> (UInt16, String.UTF8View.Index) {\n        var accumulator: UInt16 = 0\n        var digitCount = 0\n        var currentIndex = startIndex\n\n        while currentIndex < group.endIndex && digitCount < 4 {\n            let byte = group[currentIndex]\n\n            // Fast hex digit parsing using ASCII values\n            let hexValue: UInt16\n            if byte >= 48 && byte <= 57 {  // '0'-'9'\n                hexValue = UInt16(byte - 48)\n            } else if byte >= 65 && byte <= 70 {  // 'A'-'F'\n                hexValue = UInt16(byte - 65 + 10)\n            } else if byte >= 97 && byte <= 102 {  // 'a'-'f'\n                hexValue = UInt16(byte - 97 + 10)\n            } else {\n                break  // Not a hex digit\n            }\n\n            accumulator = (accumulator << 4) + hexValue\n            digitCount += 1\n            currentIndex = group.index(after: currentIndex)\n        }\n\n        guard digitCount > 0 else {\n            // No hex digits found\n            throw AddressError.invalidHexGroup\n        }\n        return (accumulator, currentIndex)\n    }\n\n    /// Parses a colon separator between IPv6 groups and detects ellipsis notation (::).\n    ///\n    /// - Parameters:\n    ///   - utf8: The UTF-8 view being parsed\n    ///   - position: Current position in the UTF-8 view (must point to a colon)\n    ///   - currentByteIndex: Current byte index in the IP array\n    ///   - ellipsisPosition: Inout parameter tracking ellipsis position\n    /// - Returns: Next position in the UTF-8 view after parsing separator\n    ///\n    /// ## Example\n    /// ```swift\n    /// let utf8 = \"2001:db8::1\".utf8\n    /// var ellipsisPos: Int? = nil\n    /// // After parsing \"2001\", position points to first ':'\n    /// let nextPos = try skipColonSeparator(from: utf8, at: position,\n    ///                                       currentByteIndex: 2,\n    ///                                       ellipsisPosition: &ellipsisPos)\n    /// // For single colon: nextPos points to 'd' in 'db8'\n    /// // For double colon (::): ellipsisPos = 2, nextPos points to '1'\n    /// ```\n    private static func skipColonSeparator(\n        from group: String.UTF8View,\n        at position: String.UTF8View.Index,\n        currentByteIndex: Int,\n        ellipsisPosition: inout Int?\n    ) throws -> String.UTF8View.Index {\n        // Expect colon separator\n        guard group[position] == 58 else {  // ASCII ':'\n            throw AddressError.malformedAddress\n        }\n\n        let afterFirstColon = group.index(after: position)\n        guard afterFirstColon < group.endIndex else {\n            // Trailing colon not allowed\n            throw AddressError.malformedAddress\n        }\n\n        // Check for double colon, return position after that\n        if group[afterFirstColon] == 58 {  // ASCII ':'\n            guard ellipsisPosition == nil else {\n                // Multiple :: not allowed\n                throw AddressError.multipleEllipsis\n            }\n            ellipsisPosition = currentByteIndex\n            let afterSecondColon = group.index(after: afterFirstColon)\n            return afterSecondColon\n        }\n        return afterFirstColon\n    }\n\n    /// Expands ellipsis for IPv6 addresses\n    ///\n    /// - Parameters:\n    ///   - ipBytes: Inout array of IP bytes to modify\n    ///   - parsedBytes: Number of bytes already parsed for IPv6 groups\n    ///   - ellipsisPosition: Optional position where ellipsis was found\n    ///   - byteLimit: Maximum bytes available for IPv6 (16 for pure IPv6, 12 if IPv4 suffix present)\n    /// - Throws: `AddressError.incompleteAddress` for invalid address lengths\n    private static func expandEllipsis(\n        in ipBytes: inout [UInt8],\n        parsedBytes: Int,\n        ellipsisPosition: Int?,\n        byteLimit: Int = 16\n    ) throws {\n        guard let ellipsisPosition = ellipsisPosition else {\n            // No ellipsis - validate we have exactly filled the available bytes\n            guard parsedBytes == byteLimit else {\n                throw AddressError.incompleteAddress  // Incomplete address without ellipsis\n            }\n            return\n        }\n\n        // Calculate expansion within the byte limit\n        let bytesToExpand = byteLimit - parsedBytes\n        guard bytesToExpand > 0 else {\n            throw AddressError.malformedAddress  // No room for ellipsis expansion\n        }\n\n        let suffixBytes = Array(ipBytes[ellipsisPosition..<parsedBytes])\n        let targetStartIndex = byteLimit - suffixBytes.count\n\n        // Clear the expansion area using Swift's range-based assignment\n        for i in ellipsisPosition..<targetStartIndex {\n            ipBytes[i] = 0\n        }\n\n        // Place suffix at the end of the IPv6 section using Swift's collection assignment\n        for (offset, byte) in suffixBytes.enumerated() {\n            ipBytes[targetStartIndex + offset] = byte\n        }\n    }\n\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/IPv6Address.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// Represents an IPv6 network address conforming to RFC 5952 and RFC 4291.\npublic struct IPv6Address: Sendable, Hashable, CustomStringConvertible, Equatable, Comparable {\n    public let value: UInt128\n\n    public let zone: String?\n\n    /// Creates an IPv6Address by parsing a string representation.\n    ///\n    /// Supports standard IPv6 formats including compressed notation (::), mixed IPv4 notation, and zone identifiers.\n    ///\n    /// - Parameter address: String representation of an IPv6 address\n    /// - Throws: `AddressError` if the string is not a valid IPv6 address\n    public init(_ address: String) throws {\n        self = try Self.parse(address)\n    }\n\n    /// Creates an IPv6Address from 16 bytes.\n    ///\n    /// - Parameters:\n    ///   - bytes: 16-byte array in network byte order representing the IPv6 address\n    ///   - zone: Optional zone identifier (e.g., \"eth0\")\n    /// - Throws: `AddressError.unableToParse` if the byte array length is not 16\n    @inlinable\n    public init(_ bytes: [UInt8], zone: String? = nil) throws {\n        guard bytes.count == 16 else {\n            throw AddressError.unableToParse\n        }\n\n        // Build UInt128 value in chunks to avoid compiler complexity\n        let hh =\n            (UInt128(bytes[0]) << 120) | (UInt128(bytes[1]) << 112) | (UInt128(bytes[2]) << 104)\n            | (UInt128(bytes[3]) << 96)\n        let hl =\n            (UInt128(bytes[4]) << 88) | (UInt128(bytes[5]) << 80) | (UInt128(bytes[6]) << 72)\n            | (UInt128(bytes[7]) << 64)\n        let lh =\n            (UInt128(bytes[8]) << 56) | (UInt128(bytes[9]) << 48) | (UInt128(bytes[10]) << 40)\n            | (UInt128(bytes[11]) << 32)\n        let ll =\n            (UInt128(bytes[12]) << 24) | (UInt128(bytes[13]) << 16) | (UInt128(bytes[14]) << 8) | UInt128(bytes[15])\n\n        self.value = hh | hl | lh | ll\n        self.zone = zone\n    }\n\n    @inlinable\n    public init(_ value: UInt128, zone: String? = nil) {\n        self.value = value\n        self.zone = zone\n    }\n\n    /// Canonical string representation following RFC 5952.\n    public var description: String {\n        // Convert UInt128 value to 16-bit groups\n        let groups: [UInt16] = [\n            UInt16((value >> 112) & 0xFFFF),\n            UInt16((value >> 96) & 0xFFFF),\n            UInt16((value >> 80) & 0xFFFF),\n            UInt16((value >> 64) & 0xFFFF),\n            UInt16((value >> 48) & 0xFFFF),\n            UInt16((value >> 32) & 0xFFFF),\n            UInt16((value >> 16) & 0xFFFF),\n            UInt16(value & 0xFFFF),\n        ]\n\n        // Find the longest run of consecutive zeros for :: compression\n        var longestZeroStart = -1\n        var longestZeroLength = 0\n        var currentZeroStart = -1\n        var currentZeroLength = 0\n\n        for (index, group) in groups.enumerated() {\n            if group == 0 {\n                if currentZeroStart == -1 {\n                    currentZeroStart = index\n                    currentZeroLength = 1\n                } else {\n                    currentZeroLength += 1\n                }\n            } else {\n                if currentZeroLength > longestZeroLength {\n                    longestZeroStart = currentZeroStart\n                    longestZeroLength = currentZeroLength\n                }\n                currentZeroStart = -1\n                currentZeroLength = 0\n            }\n        }\n        if currentZeroLength > longestZeroLength {\n            longestZeroStart = currentZeroStart\n            longestZeroLength = currentZeroLength\n        }\n\n        let useCompression = longestZeroLength >= 2\n\n        var result = \"\"\n        var index = 0\n\n        while index < 8 {\n            if useCompression && index == longestZeroStart {\n                if index == 0 {\n                    result += \"::\"\n                } else {\n                    result += \":\"\n                }\n                // Skip the compressed zeros\n                index += longestZeroLength\n\n                // If we compressed to the end, we're done\n                if index >= 8 {\n                    break\n                }\n            } else {\n                // Add the group in lowercase hex without leading zeros\n                result += String(groups[index], radix: 16, uppercase: false)\n                index += 1\n\n                // Add colon if not at the end\n                if index < 8 {\n                    result += \":\"\n                }\n            }\n        }\n\n        if let zone = zone {\n            result += \"%\" + zone\n        }\n\n        return result\n    }\n\n    @inlinable\n    public var bytes: [UInt8] {\n        Self.bytes(self.value)\n    }\n\n    @usableFromInline\n    internal static func bytes(_ value: UInt128) -> [UInt8] {\n        var result = [UInt8](repeating: 0, count: 16)\n        result[0] = UInt8((value >> 120) & 0xff)\n        result[1] = UInt8((value >> 112) & 0xff)\n        result[2] = UInt8((value >> 104) & 0xff)\n        result[3] = UInt8((value >> 96) & 0xff)\n        result[4] = UInt8((value >> 88) & 0xff)\n        result[5] = UInt8((value >> 80) & 0xff)\n        result[6] = UInt8((value >> 72) & 0xff)\n        result[7] = UInt8((value >> 64) & 0xff)\n        result[8] = UInt8((value >> 56) & 0xff)\n        result[9] = UInt8((value >> 48) & 0xff)\n        result[10] = UInt8((value >> 40) & 0xff)\n        result[11] = UInt8((value >> 32) & 0xff)\n        result[12] = UInt8((value >> 24) & 0xff)\n        result[13] = UInt8((value >> 16) & 0xff)\n        result[14] = UInt8((value >> 8) & 0xff)\n        result[15] = UInt8(value & 0xff)\n        return result\n    }\n\n    @available(macOS 26.0, *)\n    @usableFromInline\n    internal static func bytes(_ value: UInt128) -> InlineArray<16, UInt8> {\n        let result: InlineArray<16, UInt8> = [\n            UInt8((value >> 120) & 0xff),\n            UInt8((value >> 112) & 0xff),\n            UInt8((value >> 104) & 0xff),\n            UInt8((value >> 96) & 0xff),\n            UInt8((value >> 88) & 0xff),\n            UInt8((value >> 80) & 0xff),\n            UInt8((value >> 72) & 0xff),\n            UInt8((value >> 64) & 0xff),\n            UInt8((value >> 56) & 0xff),\n            UInt8((value >> 48) & 0xff),\n            UInt8((value >> 40) & 0xff),\n            UInt8((value >> 32) & 0xff),\n            UInt8((value >> 24) & 0xff),\n            UInt8((value >> 16) & 0xff),\n            UInt8((value >> 8) & 0xff),\n            UInt8(value & 0xff),\n        ]\n        return result\n    }\n\n    /// The unspecified IPv6 address (::)\n    public static let unspecified = IPv6Address(0)\n\n    /// The loopback IPv6 address (::1)\n    public static let loopback = IPv6Address(1)\n\n    // MARK: - Address Classification Methods\n\n    /// Returns `true` if this is the unspecified address (::).\n    @inlinable\n    public var isUnspecified: Bool {\n        value == 0\n    }\n\n    /// Returns `true` if this is the loopback address (::1).\n    @inlinable\n    public var isLoopback: Bool {\n        value == 1\n    }\n\n    /// Returns `true` if this is a multicast address (ff00::/8).\n    @inlinable\n    public var isMulticast: Bool {\n        (value >> 120) == 0xFF\n    }\n\n    /// Returns `true` if this is a link-local unicast address (fe80::/10).\n    @inlinable\n    public var isLinkLocal: Bool {\n        (value >> 118) == 0x3FA  // fe80::/10 = top 10 bits are 1111111010\n    }\n\n    /// Returns `true` if this is a unique local address (fc00::/7).\n    @inlinable\n    public var isUniqueLocal: Bool {\n        (value >> 121) == 0x7E  // fc00::/7 = top 7 bits are 1111110\n    }\n\n    /// Returns `true` if this is a global unicast address.\n    @inlinable\n    public var isGlobalUnicast: Bool {\n        !isUnspecified && !isLoopback && !isMulticast && !isLinkLocal && !isUniqueLocal\n    }\n\n    /// Returns `true` if this is a documentation address (2001:db8::/32).\n    @inlinable\n    public var isDocumentation: Bool {\n        (value >> 96) == 0x2001_0DB8  // 2001:db8::/32\n    }\n    /// Compares two IPv6 addresses numerically, then by zone if values are equal.\n    @inlinable\n    public static func < (lhs: IPv6Address, rhs: IPv6Address) -> Bool {\n        if lhs.value != rhs.value {\n            return lhs.value < rhs.value\n        }\n        // Same value, compare zones lexicographically\n        return (lhs.zone ?? \"\") < (rhs.zone ?? \"\")\n    }\n}\n\nextension IPv6Address: Codable {\n    public init(from decoder: Decoder) throws {\n        let container = try decoder.singleValueContainer()\n        let string = try container.decode(String.self)\n        try self.init(string)\n    }\n\n    public func encode(to encoder: Encoder) throws {\n        var container = encoder.singleValueContainer()\n        try container.encode(description)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/IndexedAddressAllocator.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Collections\nimport Synchronization\n\n/// Maps a network address to an array index value, or nil in the case of a domain error.\npackage typealias AddressToIndexTransform<AddressType> = @Sendable (AddressType) -> Int?\n\n/// Maps an array index value to a network address, or nil in the case of a domain error.\npackage typealias IndexToAddressTransform<AddressType> = @Sendable (Int) -> AddressType?\n\npackage final class IndexedAddressAllocator<AddressType: CustomStringConvertible & Sendable>: AddressAllocator {\n    private class State {\n        var allocations: BitArray\n        var enabled: Bool\n        var allocationCount: Int\n        let addressToIndex: AddressToIndexTransform<AddressType>\n        let indexToAddress: IndexToAddressTransform<AddressType>\n\n        init(\n            size: Int,\n            addressToIndex: @escaping AddressToIndexTransform<AddressType>,\n            indexToAddress: @escaping IndexToAddressTransform<AddressType>\n        ) {\n            self.allocations = BitArray.init(repeating: false, count: size)\n            self.enabled = true\n            self.allocationCount = 0\n            self.addressToIndex = addressToIndex\n            self.indexToAddress = indexToAddress\n        }\n    }\n\n    private let state: Mutex<State>\n\n    /// Create an allocator with specified size and index mappings.\n    package init(\n        size: Int,\n        addressToIndex: @escaping AddressToIndexTransform<AddressType>,\n        indexToAddress: @escaping IndexToAddressTransform<AddressType>\n    ) {\n        let state = State(\n            size: size,\n            addressToIndex: addressToIndex,\n            indexToAddress: indexToAddress\n        )\n        self.state = Mutex(state)\n    }\n\n    public func allocate() throws -> AddressType {\n        try self.state.withLock { state in\n            guard state.enabled else {\n                throw AllocatorError.allocatorDisabled\n            }\n\n            guard let index = state.allocations.firstIndex(of: false) else {\n                throw AllocatorError.allocatorFull\n            }\n\n            guard let address = state.indexToAddress(index) else {\n                throw AllocatorError.invalidIndex(index)\n            }\n\n            state.allocations[index] = true\n            state.allocationCount += 1\n            return address\n        }\n    }\n\n    package func reserve(_ address: AddressType) throws {\n        try self.state.withLock { state in\n            guard state.enabled else {\n                throw AllocatorError.allocatorDisabled\n            }\n\n            guard let index = state.addressToIndex(address) else {\n                throw AllocatorError.invalidAddress(address.description)\n            }\n\n            guard !state.allocations[index] else {\n                throw AllocatorError.alreadyAllocated(\"\\(address.description)\")\n            }\n\n            state.allocations[index] = true\n            state.allocationCount += 1\n        }\n\n    }\n\n    package func release(_ address: AddressType) throws {\n        try self.state.withLock { state in\n            guard let index = state.addressToIndex(address) else {\n                throw AllocatorError.invalidAddress(address.description)\n            }\n\n            guard state.allocations[index] else {\n                throw AllocatorError.notAllocated(\"\\(address.description)\")\n            }\n\n            state.allocations[index] = false\n            state.allocationCount -= 1\n        }\n    }\n\n    package func disableAllocator() -> Bool {\n        self.state.withLock { state in\n            guard state.allocationCount == 0 else {\n                return false\n            }\n            state.enabled = false\n            return true\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/MACAddress.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// An EUI-48 MAC address as specified by IEEE 802.\n@frozen\npublic struct MACAddress: Sendable, Hashable, CustomStringConvertible, Equatable, Comparable {\n    public let value: UInt64\n\n    /// Creates an MACAddress from an integer.\n    ///\n    /// - Parameter value: The big-endian value of the MAC address.\n    ///   The most significant 16 bits of the value are ignored.\n    @inlinable\n    public init(_ value: UInt64) {\n        self.value = value & 0x0000_ffff_ffff_ffff\n    }\n\n    /// Creates an IPv4Address from 6 bytes.\n    ///\n    /// - Parameters:\n    ///   - bytes: 6-byte array in network byte order representing the IPv4 address\n    /// - Throws: `AddressError.unableToParse` if the byte array length is not 6\n    @inlinable\n    public init(_ bytes: [UInt8]) throws {\n        guard bytes.count == 6 else {\n            throw AddressError.unableToParse\n        }\n        self.value =\n            (UInt64(bytes[0]) << 40)\n            | (UInt64(bytes[1]) << 32)\n            | (UInt64(bytes[2]) << 24)\n            | (UInt64(bytes[3]) << 16)\n            | (UInt64(bytes[4]) << 8)\n            | UInt64(bytes[5])\n    }\n\n    /// Creates an MACAddress from a string representation.\n    ///\n    /// - Parameter string: The MAC address string with colon or dash delimiters.\n    /// - Throws: `AddressError.unableToParse` if the string is not a valid MAC address\n    @inlinable\n    public init(_ string: String) throws {\n        self.value = try Self.parse(string)\n    }\n\n    @inlinable\n    public var bytes: [UInt8] {\n        Self.bytes(value)\n    }\n\n    @usableFromInline\n    static func bytes(_ value: UInt64) -> [UInt8] {\n        var result = [UInt8](repeating: 0, count: 6)\n        result[0] = UInt8((value >> 40) & 0xff)\n        result[1] = UInt8((value >> 32) & 0xff)\n        result[2] = UInt8((value >> 24) & 0xff)\n        result[3] = UInt8((value >> 16) & 0xff)\n        result[4] = UInt8((value >> 8) & 0xff)\n        result[5] = UInt8(value & 0xff)\n        return result\n    }\n\n    @available(macOS 26.0, *)\n    @usableFromInline\n    static func bytes(_ value: UInt64) -> InlineArray<6, UInt8> {\n        let result: InlineArray<6, UInt8> = [\n            UInt8((value >> 40) & 0xff),\n            UInt8((value >> 32) & 0xff),\n            UInt8((value >> 24) & 0xff),\n            UInt8((value >> 16) & 0xff),\n            UInt8((value >> 8) & 0xff),\n            UInt8(value & 0xff),\n        ]\n        return result\n    }\n\n    @inlinable\n    public var description: String {\n        bytes.map { String(format: \"%02x\", $0) }.joined(separator: \":\")\n    }\n\n    /// Parses an MAC address string into a UInt64 representation.\n    ///\n    /// ## Validation Rules\n    /// - Exactly six groups of two hexadecimal digits, separated by colons\n    ///   or dashes\n    /// - No whitespace characters\n    /// - Only hexadecimal digits and colons allowed\n    ///\n    /// ## Examples\n    /// ```swift\n    /// MACAddress.parse(\"01:23:45:67:89:ab\") // Returns: 0x0000_0123_4567_89ab\n    /// MACAddress.parse(\"01-23-45-67-89-AB\") // Returns: 0x0000_0123_4567_89ab\n    /// MACAddress.parse(\"00:00:00:00:00:00\") // Returns: 0x0000_0000_0000_0000\n    /// MACAddress.parse(\"ff:ff:ff:ff:ff:ff\") // Returns: 0x0000_ffff_ffff_ffff\n    ///\n    /// // Invalid examples:\n    /// MACAddress.parse(\"01:23:45:67:89\")    // Wrong number of octets\n    /// MACAddress.parse(\"01:23:45:67:89:a\")  // Invalid octet length\n    /// MACAddress.parse(\"01:23:45:67:89:hi\") // Invalid octet content\n    /// MACAddress.parse(\"01:23-45:67-89:ab\") // Inconsistent separators\n    /// MACAddress.parse(\" 01:23:45:67:89:ab \") // Whitespace\n    /// ```\n    ///\n    /// - Parameter s: The MAC address string to parse\n    /// - Returns: The 64-bit representation of the IP address, or `nil` if parsing fails\n    /// - Note: The returned value is in network byte order (big-endian)\n    @usableFromInline\n    internal static func parse(_ s: String) throws -> UInt64 {\n        guard !s.isEmpty, s.count == 17 else {\n            throw AddressError.unableToParse\n        }\n\n        // MAC addresses should only contain ASCII hex digits and dots\n        let utf8 = s.utf8\n        for byte in utf8 {\n            // ASCII whitespace: space(32), tab(9), newline(10), return(13)\n            if byte == 32 || byte == 9 || byte == 10 || byte == 13 {\n                throw AddressError.unableToParse\n            }\n        }\n\n        // accumulator for the 64 bit representation of the MAC address\n        var result: UInt64 = 0\n\n        // tracking octet count, max 6 allowed\n        var octetCount = 0\n        var currentOctet = 0\n\n        // number of digits in the string representation of the octet\n        var digitCount = 0\n\n        // separator character to use\n        var separator: String.UTF8View.Element?\n\n        for byte in utf8 {\n            if byte == 0x3a || byte == 0x2d {  // ASCII ':'\n                // Ensure separator is consistent\n                guard separator == nil || byte == separator else {\n                    throw AddressError.unableToParse\n                }\n                separator = byte\n\n                // Validate octet before processing\n                guard octetCount < 5, digitCount == 2 else {\n                    throw AddressError.unableToParse\n                }\n\n                // Shift result and add current octet\n                result = (result << 8) | UInt64(currentOctet)\n\n                // Reset for next octet\n                octetCount += 1\n                currentOctet = 0\n                digitCount = 0\n\n            } else if byte >= 0x30 && byte <= 0x39 {  // ASCII '0'-'9'\n                let digit = Int(byte - 0x30)\n\n                digitCount += 1\n                currentOctet = (currentOctet << 4) + digit\n\n                // Early termination if octet becomes too large\n                guard digitCount <= 2 else {\n                    throw AddressError.unableToParse\n                }\n\n            } else if byte >= 0x41 && byte <= 0x46 {  // ASCII 'A'-'F'\n                let digit = Int(byte - 0x41 + 10)\n\n                digitCount += 1\n                currentOctet = (currentOctet << 4) + digit\n\n                // Early termination if octet becomes too large\n                guard digitCount <= 2 else {\n                    throw AddressError.unableToParse\n                }\n\n            } else if byte >= 0x61 && byte <= 0x66 {  // ASCII 'A'-'F'\n                let digit = Int(byte - 0x61 + 10)\n\n                digitCount += 1\n                currentOctet = (currentOctet << 4) + digit\n\n                // Early termination if octet becomes too large\n                guard digitCount <= 2 else {\n                    throw AddressError.unableToParse\n                }\n\n            } else {\n                throw AddressError.unableToParse\n            }\n        }\n\n        // Validate final octet\n        guard octetCount == 5, digitCount == 2 else {\n            throw AddressError.unableToParse\n        }\n\n        return (result << 8) | UInt64(currentOctet)\n    }\n\n    // MARK: - Address Classification Methods\n\n    /// Returns `true` if the MAC address is locally administered.\n    ///\n    /// IEEE 802 specifies that the second-least-significant bit of\n    /// the first octet of the MAC address determines whether the\n    /// address is globally unique (bit cleared) or locally\n    /// administered (bit set).\n    @inlinable\n    public var isLocallyAdministered: Bool {\n        (value & 0x0000_0200_0000_0000) != 0\n    }\n\n    /// Returns `true` if the MAC address is multicast.\n    ///\n    /// IEEE 802 specifies that the least-significant bit of\n    /// the first octet of the MAC address determines whether the\n    /// address is unicast (bit cleared) or multicast (bit set).\n    @inlinable\n    public var isMulticast: Bool {\n        (value & 0x0000_0100_0000_0000) != 0\n    }\n\n    /// Returns the link local IP address based on the EUI-64 version\n    /// of the MAC address.\n    ///\n    /// - Parameter network: The IPv6 address to use for the network prefix\n    /// - Returns: The link local IP address for the MAC address\n    @inlinable\n    public func ipv6Address(network: IPv6Address) throws -> IPv6Address {\n        let prefixBytes = network.bytes\n        return try IPv6Address([\n            prefixBytes[0], prefixBytes[1], prefixBytes[2], prefixBytes[3],\n            prefixBytes[4], prefixBytes[5], prefixBytes[6], prefixBytes[7],\n            bytes[0] ^ 0x02, bytes[1], bytes[2], 0xff,\n            0xfe, bytes[3], bytes[4], bytes[5],\n        ])\n    }\n\n    /// Compares two IPv4 addresses numerically.\n    @inlinable\n    public static func < (lhs: MACAddress, rhs: MACAddress) -> Bool {\n        lhs.value < rhs.value\n    }\n}\n\nextension MACAddress: Codable {\n    public init(from decoder: Decoder) throws {\n        let container = try decoder.singleValueContainer()\n        let string = try container.decode(String.self)\n        try self.init(string)\n    }\n\n    public func encode(to encoder: Encoder) throws {\n        var container = encoder.singleValueContainer()\n        try container.encode(description)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/NetworkAddress+Allocator.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nextension IPv4Address {\n    /// Creates an allocator for IPv4 addresses.\n    public static func allocator(lower: UInt32, size: Int) throws -> any AddressAllocator<IPv4Address> {\n        // NOTE: 2^31 - 1 size limit in the very improbable case that we run on 32-bit.\n        guard size > 0 && size < Int.max && 0xffff_ffff - lower >= size - 1 else {\n            throw AllocatorError.rangeExceeded\n        }\n        return IndexedAddressAllocator(\n            size: size,\n            addressToIndex: { address in\n                guard address.value >= lower && address.value - lower <= UInt32(size) else {\n                    return nil\n                }\n                return Int(address.value - lower)\n            },\n            indexToAddress: { IPv4Address(lower + UInt32($0)) }\n        )\n    }\n}\n\nextension UInt16 {\n    /// Creates an allocator for TCP/UDP ports and other UInt16 values.\n    public static func allocator(lower: UInt16, size: Int) throws -> any AddressAllocator<UInt16> {\n        guard 0xffff - lower + 1 >= size else {\n            throw AllocatorError.rangeExceeded\n        }\n\n        return IndexedAddressAllocator(\n            size: size,\n            addressToIndex: { address in\n                guard address >= lower && address <= lower + UInt16(size) else {\n                    return nil\n                }\n                return Int(address - lower)\n            },\n            indexToAddress: { lower + UInt16($0) }\n        )\n    }\n}\n\nextension UInt32 {\n    /// Creates an allocator for vsock ports, or any UInt32 values.\n    public static func allocator(lower: UInt32, size: Int) throws -> any AddressAllocator<UInt32> {\n        guard 0xffff_ffff - lower + 1 >= size else {\n            throw AllocatorError.rangeExceeded\n        }\n\n        return IndexedAddressAllocator(\n            size: size,\n            addressToIndex: { address in\n                guard address >= lower && address <= lower + UInt32(size) else {\n                    return nil\n                }\n                return Int(address - lower)\n            },\n            indexToAddress: { lower + UInt32($0) }\n        )\n    }\n\n    /// Creates a rotating allocator for vsock ports, or any UInt32 values.\n    public static func rotatingAllocator(lower: UInt32, size: UInt32) throws -> any AddressAllocator<UInt32> {\n        guard 0xffff_ffff - lower + 1 >= size else {\n            throw AllocatorError.rangeExceeded\n        }\n\n        return RotatingAddressAllocator(\n            size: size,\n            addressToIndex: { address in\n                guard address >= lower && address <= lower + UInt32(size) else {\n                    return nil\n                }\n                return Int(address - lower)\n            },\n            indexToAddress: { lower + UInt32($0) }\n        )\n    }\n}\n\nextension Character {\n    private static let deviceLetters = Array(\"abcdefghijklmnopqrstuvwxyz\")\n\n    /// Creates an allocator for block device tags, or any character values.\n    public static func blockDeviceTagAllocator() -> any AddressAllocator<Character> {\n        IndexedAddressAllocator(\n            size: Self.deviceLetters.count,\n            addressToIndex: { address in\n                Self.deviceLetters.firstIndex(of: address)\n            },\n            indexToAddress: { Self.deviceLetters[$0] }\n        )\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/Prefix.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// CIDR prefix length (e.g., `/24` for a 24-bit network mask).\n@frozen\npublic struct Prefix: Sendable, CustomStringConvertible, Hashable, Codable {\n    public let length: UInt8\n\n    /// Create a prefix (0-128). Use `ipv4(_:)` or `ipv6(_:)` for version-specific validation.\n    public init?(length: UInt8) {\n        guard length <= 128 else { return nil }\n        self.length = length\n    }\n\n    /// Create an IPv4 prefix (0-32). Returns `nil` if length > 32.\n    public static func ipv4(_ length: UInt8) -> Prefix? {\n        guard length <= 32 else { return nil }\n        return Prefix(unchecked: length)\n    }\n\n    /// Create an IPv6 prefix (0-128). Returns `nil` if length > 128.\n    public static func ipv6(_ length: UInt8) -> Prefix? {\n        guard length <= 128 else { return nil }\n        return Prefix(unchecked: length)\n    }\n\n    /// Internal unchecked initializer for known-valid values.\n    internal init(unchecked length: UInt8) {\n        self.length = length\n    }\n\n    public var description: String {\n        \"\\(length)\"\n    }\n}\n\nextension Prefix {\n    /// Computes a 32-bit mask for the suffix (host) portion of an IPv4 address.\n    ///\n    /// Example: Prefix `/24` → `0x0000_00FF` (255 host addresses)\n    @inlinable\n    public var suffixMask32: UInt32 {\n        if self.length <= 0 {\n            return 0xffff_ffff\n        }\n        return self.length >= 32 ? 0x0000_0000 : (1 << (32 - self.length)) - 1\n    }\n\n    /// Network portion mask (high-order bits) for IPv4.\n    ///\n    /// Example: Prefix `/24` → `0xFFFF_FF00` (255.255.255.0)\n    @inlinable\n    public var prefixMask32: UInt32 {\n        ~self.suffixMask32\n    }\n\n    /// Computes a 128-bit mask for the suffix (host) portion of an IPv6 address.\n    ///\n    /// Example: Prefix `/64` → `0x0000_0000_0000_0000_FFFF_FFFF_FFFF_FFFF`\n    @inlinable\n    public var suffixMask128: UInt128 {\n        if self.length <= 0 {\n            return UInt128.max\n        }\n        return self.length >= 128 ? 0 : (1 << (128 - self.length)) - 1\n    }\n\n    /// Network portion mask (high-order bits) for IPv6.\n    ///\n    /// Example: Prefix `/64` → `0xFFFF_FFFF_FFFF_FFFF_0000_0000_0000_0000`\n    @inlinable\n    public var prefixMask128: UInt128 {\n        ~self.suffixMask128\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/ProgressEvent.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// A progress update event.\npublic enum ProgressEvent: Sendable {\n    /// The possible values:\n    ///  - `add-items`: Increment the number of processed items by `value`.\n    ///  - `add-total-items`: Increment the total number of items to process by `value`.\n    ///  - `add-size`: Increment the size of processed items by `value`.\n    ///  - `add-total-size`: Increment the total size of items to process by `value`.\n    case addItems(Int)\n    case addTotalItems(Int)\n    case addSize(Int64)\n    case addTotalSize(Int64)\n\n    /// The event name.\n    public var event: String {\n        switch self {\n        case .addItems: \"add-items\"\n        case .addTotalItems: \"add-total-items\"\n        case .addSize: \"add-size\"\n        case .addTotalSize: \"add-total-size\"\n        }\n    }\n\n    /// The event value.\n    public var value: any Sendable {\n        switch self {\n        case .addItems(let value): value\n        case .addTotalItems(let value): value\n        case .addSize(let value): value\n        case .addTotalSize(let value): value\n        }\n    }\n}\n\n/// The progress update handler.\npublic typealias ProgressHandler = @Sendable (_ events: [ProgressEvent]) async -> Void\n"
  },
  {
    "path": "Sources/ContainerizationExtras/ProxyUtils.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport Foundation\n\n/// A small utility to resolve proxy settings (HTTP(S)_PROXY / NO_PROXY).\npublic enum ProxyUtils {\n    /// Resolves the proxy URL for a given host based on environment variables.\n    /// Malformed http_proxy or https_proxy URLs are ignored.\n    /// Uses Go-style handling rules:\n    ///   - Uppercase environment variables take priority over lowercase counterparts.\n    ///   - Leading dot on no_proxy component implies prefix matching.\n    ///\n    /// - Parameters:\n    ///   - scheme: The request scheme.\n    ///   - host: The request hostname.\n    ///   - env: Environment variables to check, dafaulting to the process environment.\n    ///\n    /// - Returns: The proxy URL to use, or `nil` for transparent connection.\n    public static func proxyFromEnvironment(\n        scheme: String?,\n        host: String,\n        env: [String: String] = ProcessInfo.processInfo.environment\n    ) -> URL? {\n        guard let scheme else {\n            return nil\n        }\n\n        let httpProxy = env[\"HTTP_PROXY\"] ?? env[\"http_proxy\"]\n        let httpsProxy = env[\"HTTPS_PROXY\"] ?? env[\"https_proxy\"]\n        let noProxy = env[\"NO_PROXY\"] ?? env[\"no_proxy\"]\n\n        // If NO_PROXY matches → skip proxy\n        if let noProxy, shouldBypassProxy(host: host, noProxy: noProxy) {\n            return nil\n        }\n\n        // Select proxy based on scheme, defaulting to http.\n        let proxy = scheme == \"https\" ? httpsProxy : httpProxy\n        guard let proxy, let proxyUrl = URL(string: proxy) else {\n            return nil\n        }\n\n        return proxyUrl\n    }\n\n    /// Check if a host should bypass proxy according to NO_PROXY.\n    /// - Example: NO_PROXY=\".example.com,localhost,127.0.0.1\"\n    private static func shouldBypassProxy(host: String, noProxy: String) -> Bool {\n        let entries = noProxy.split(separator: \",\").map { $0.trimmingCharacters(in: .whitespaces) }\n        for entry in entries {\n            if entry.isEmpty { continue }\n            if entry == \"*\" { return true }\n            if host == entry { return true }\n            if entry.hasPrefix(\"*.\") {\n                let suffix = String(entry.dropFirst())\n                if host.hasSuffix(suffix) { return true }\n            }\n            if entry.hasPrefix(\".\") && host.hasSuffix(entry) { return true }\n        }\n        return false\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/RotatingAddressAllocator.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Synchronization\n\npackage final class RotatingAddressAllocator: AddressAllocator {\n    package typealias AddressType = UInt32\n\n    private struct State {\n        var allocations: [AddressType]\n        var enabled: Bool\n        var allocationCount: Int\n        let addressToIndex: AddressToIndexTransform<AddressType>\n        let indexToAddress: IndexToAddressTransform<AddressType>\n\n        init(\n            size: UInt32,\n            addressToIndex: @escaping AddressToIndexTransform<AddressType>,\n            indexToAddress: @escaping IndexToAddressTransform<AddressType>\n        ) {\n            self.allocations = [UInt32](0..<size)\n            self.enabled = true\n            self.allocationCount = 0\n            self.addressToIndex = addressToIndex\n            self.indexToAddress = indexToAddress\n        }\n    }\n\n    private let state: Mutex<State>\n\n    /// Create an allocator with specified size and index mappings.\n    package init(\n        size: UInt32,\n        addressToIndex: @escaping AddressToIndexTransform<AddressType>,\n        indexToAddress: @escaping IndexToAddressTransform<AddressType>\n    ) {\n        let state = State(\n            size: size,\n            addressToIndex: addressToIndex,\n            indexToAddress: indexToAddress\n        )\n        self.state = Mutex(state)\n    }\n\n    public func allocate() throws -> AddressType {\n        try self.state.withLock { state in\n            guard state.enabled else {\n                throw AllocatorError.allocatorDisabled\n            }\n\n            guard state.allocations.count > 0 else {\n                throw AllocatorError.allocatorFull\n            }\n\n            let value = state.allocations.removeFirst()\n\n            guard let address = state.indexToAddress(Int(value)) else {\n                throw AllocatorError.invalidIndex(Int(value))\n            }\n\n            state.allocationCount += 1\n            return address\n        }\n    }\n\n    package func reserve(_ address: AddressType) throws {\n        try self.state.withLock { state in\n            guard state.enabled else {\n                throw AllocatorError.allocatorDisabled\n            }\n\n            guard let index = state.addressToIndex(address) else {\n                throw AllocatorError.invalidAddress(address.description)\n            }\n\n            let i = state.allocations.firstIndex(of: UInt32(index))\n            guard let i else {\n                throw AllocatorError.alreadyAllocated(\"\\(address.description)\")\n            }\n\n            _ = state.allocations.remove(at: i)\n            state.allocationCount += 1\n        }\n    }\n\n    package func release(_ address: AddressType) throws {\n        try self.state.withLock { state in\n            guard let index = (state.addressToIndex(address)) else {\n                throw AllocatorError.invalidAddress(address.description)\n            }\n            let value = UInt32(index)\n\n            guard !state.allocations.contains(value) else {\n                throw AllocatorError.notAllocated(\"\\(address.description)\")\n            }\n\n            state.allocations.append(value)\n            state.allocationCount -= 1\n        }\n    }\n\n    package func disableAllocator() -> Bool {\n        self.state.withLock { state in\n            guard state.allocationCount == 0 else {\n                return false\n            }\n            state.enabled = false\n            return true\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/TLSUtils.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport NIO\nimport NIOSSL\n\npublic enum TLSUtils {\n\n    public static func makeEnvironmentAwareTLSConfiguration() -> TLSConfiguration {\n        var tlsConfig = TLSConfiguration.makeClientConfiguration()\n\n        // Check standard SSL environment variables in priority order\n        let customCAPath =\n            ProcessInfo.processInfo.environment[\"SSL_CERT_FILE\"]\n            ?? ProcessInfo.processInfo.environment[\"CURL_CA_BUNDLE\"]\n            ?? ProcessInfo.processInfo.environment[\"REQUESTS_CA_BUNDLE\"]\n\n        if let caPath = customCAPath {\n            tlsConfig.trustRoots = .file(caPath)\n        }\n        // else: use .default\n\n        return tlsConfig\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/Timeout.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// `Timeout` contains helpers to run an operation and error out if\n/// the operation does not finish within a provided time.\npublic struct Timeout {\n    /// Performs the passed in `operation` and throws a `CancellationError` if the operation\n    /// doesn't finish in the provided `seconds` amount.\n    public static func run<T: Sendable>(\n        seconds: UInt32,\n        operation: @escaping @Sendable () async throws -> T\n    ) async throws -> T {\n        try await withThrowingTaskGroup(of: T.self) { group in\n            group.addTask {\n                try await operation()\n            }\n\n            group.addTask {\n                try await Task.sleep(for: .seconds(seconds))\n                throw CancellationError()\n            }\n\n            guard let result = try await group.next() else {\n                fatalError()\n            }\n\n            group.cancelAll()\n            return result\n        }\n    }\n\n    public static func run<T: Sendable>(\n        for duration: Duration,\n        operation: @escaping @Sendable () async throws -> T\n    ) async throws -> T {\n        try await withThrowingTaskGroup(of: T.self) { group in\n            group.addTask {\n                try await operation()\n            }\n\n            group.addTask {\n                try await Task.sleep(for: duration)\n                throw CancellationError()\n            }\n\n            guard let result = try await group.next() else {\n                fatalError()\n            }\n\n            group.cancelAll()\n            return result\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationExtras/UInt8+DataBinding.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\npackage enum BindError: Error, CustomStringConvertible {\n    case recvMarshalFailure(type: String, field: String)\n    case sendMarshalFailure(type: String, field: String)\n\n    package var description: String {\n        switch self {\n        case .recvMarshalFailure(let type, let field):\n            return \"failed to unmarshal \\(type).\\(field)\"\n        case .sendMarshalFailure(let type, let field):\n            return \"failed to marshal \\(type).\\(field)\"\n        }\n    }\n}\n\npackage protocol Bindable: Sendable {\n    static var size: Int { get }\n    func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int\n    mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int\n}\n\nextension ArraySlice<UInt8> {\n    package func hexEncodedString() -> String {\n        self.map { String(format: \"%02hhx\", $0) }.joined()\n    }\n}\n\nextension [UInt8] {\n    package func hexEncodedString() -> String {\n        self.map { String(format: \"%02hhx\", $0) }.joined()\n    }\n\n    package mutating func bind<T>(as type: T.Type, offset: Int = 0, size: Int? = nil) -> UnsafeMutablePointer<T>? {\n        guard self.count >= (size ?? MemoryLayout<T>.size) + offset else {\n            return nil\n        }\n\n        return self.withUnsafeMutableBytes { $0.baseAddress?.advanced(by: offset).assumingMemoryBound(to: T.self) }\n    }\n\n    package mutating func copyIn<T>(as type: T.Type, value: T, offset: Int = 0, size: Int? = nil) -> Int? {\n        let size = size ?? MemoryLayout<T>.size\n        guard self.count >= size + offset else {\n            return nil\n        }\n\n        return self.withUnsafeMutableBytes {\n            $0.baseAddress?.advanced(by: offset).assumingMemoryBound(to: T.self).pointee = value\n            return offset + MemoryLayout<T>.size\n        }\n    }\n\n    package func copyOut<T>(as type: T.Type, offset: Int = 0, size: Int? = nil) -> (Int, T)? {\n        guard self.count >= (size ?? MemoryLayout<T>.size) + offset else {\n            return nil\n        }\n\n        return self.withUnsafeBytes {\n            guard let value = $0.baseAddress?.advanced(by: offset).assumingMemoryBound(to: T.self).pointee else {\n                return nil\n            }\n            return (offset + MemoryLayout<T>.size, value)\n        }\n    }\n\n    package mutating func copyIn(buffer: [UInt8], offset: Int = 0) -> Int? {\n        guard offset + buffer.count <= self.count else {\n            return nil\n        }\n\n        self[offset..<offset + buffer.count] = buffer[0..<buffer.count]\n        return offset + buffer.count\n    }\n\n    package func copyOut(buffer: inout [UInt8], offset: Int = 0) -> Int? {\n        guard offset + buffer.count <= self.count else {\n            return nil\n        }\n\n        buffer[0..<buffer.count] = self[offset..<offset + buffer.count]\n        return offset + buffer.count\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationIO/ReadStream.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOS\nimport Foundation\nimport NIO\n\n/// `ReadStream` is a utility type for streaming data from a `URL`\n/// or `Data` blob.\npublic class ReadStream {\n    public static let bufferSize = Int(1.mib())\n\n    private var _stream: InputStream\n    private let buffSize: Int\n    private let data: Data?\n    private let url: URL?\n\n    public init() {\n        _stream = InputStream(data: .init())\n        buffSize = Self.bufferSize\n        self.data = Data()\n        self.url = nil\n    }\n\n    public init(url: URL, bufferSize: Int = bufferSize) throws {\n        guard FileManager.default.fileExists(atPath: url.path) else {\n            throw Error.noSuchFileOrDirectory(url)\n        }\n        guard let stream = InputStream(url: url) else {\n            throw Error.failedToCreateStream\n        }\n        self._stream = stream\n        self.buffSize = bufferSize\n        self.url = url\n        self.data = nil\n    }\n\n    public init(data: Data, bufferSize: Int = bufferSize) {\n        self._stream = InputStream(data: data)\n        self.buffSize = bufferSize\n        self.url = nil\n        self.data = data\n    }\n\n    /// Resets the read stream. This either reassigns\n    /// the data buffer or url to a new InputStream internally.\n    public func reset() throws {\n        self._stream.close()\n        if let url = self.url {\n            guard let s = InputStream(url: url) else {\n                throw Error.failedToCreateStream\n            }\n            self._stream = s\n            return\n        }\n        let data = self.data ?? Data()\n        self._stream = InputStream(data: data)\n    }\n\n    /// Get access to an `AsyncStream` of `ByteBuffer`'s from the input source.\n    public var stream: AsyncStream<ByteBuffer> {\n        AsyncStream { cont in\n            self._stream.open()\n            defer { self._stream.close() }\n\n            let readBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: buffSize)\n\n            while true {\n                let byteRead = self._stream.read(readBuffer, maxLength: buffSize)\n                if byteRead <= 0 {\n                    readBuffer.deallocate()\n                    cont.finish()\n                    break\n                } else {\n                    let data = Data(bytes: readBuffer, count: byteRead)\n                    let buffer = ByteBuffer(bytes: data)\n                    cont.yield(buffer)\n                }\n            }\n        }\n    }\n\n    /// Get access to an `AsyncStream` of `Data` objects from the input source.\n    public var dataStream: AsyncStream<Data> {\n        AsyncStream { cont in\n            self._stream.open()\n            defer { self._stream.close() }\n\n            let readBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: self.buffSize)\n            while true {\n                let byteRead = self._stream.read(readBuffer, maxLength: self.buffSize)\n                if byteRead <= 0 {\n                    readBuffer.deallocate()\n                    cont.finish()\n                    break\n                } else {\n                    let data = Data(bytes: readBuffer, count: byteRead)\n                    cont.yield(data)\n                }\n            }\n        }\n    }\n}\n\nextension ReadStream {\n    /// Errors that can be encountered while using a `ReadStream`.\n    public enum Error: Swift.Error, CustomStringConvertible {\n        case failedToCreateStream\n        case noSuchFileOrDirectory(_ p: URL)\n\n        public var description: String {\n            switch self {\n            case .failedToCreateStream:\n                return \"failed to create stream\"\n            case .noSuchFileOrDirectory(let p):\n                return \"no such file or directory: \\(p.path)\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationNetlink/NetlinkSession.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationExtras\nimport ContainerizationOS\nimport Logging\n\n/// `NetlinkSession` facilitates interacting with netlink via a provided `NetlinkSocket`. This is\n/// the core high-level type offered to perform actions to the netlink surface in the kernel.\npublic struct NetlinkSession {\n    private static let receiveDataLength = 65536\n    private static let mtu: UInt32 = 1280\n    private let socket: any NetlinkSocket\n    private let log: Logger\n\n    /// Creates a new `NetlinkSession`.\n    /// - Parameters:\n    ///   - socket: The `NetlinkSocket` to use for netlink interaction.\n    ///   - log: The logger to use. The default value is `nil`.\n    public init(socket: any NetlinkSocket, log: Logger? = nil) {\n        self.socket = socket\n        self.log = log ?? Logger(label: \"com.apple.containerization.netlink\")\n    }\n\n    /// Errors that may occur during netlink interaction.\n    public enum Error: Swift.Error, CustomStringConvertible, Equatable {\n        case invalidIpAddress\n        case invalidPrefixLength\n        case unexpectedInfo(type: UInt16)\n        case unexpectedOffset(offset: Int, size: Int)\n        case unexpectedResidualPackets\n        case unexpectedResultSet(count: Int, expected: Int)\n\n        /// The description of the errors.\n        public var description: String {\n            switch self {\n            case .invalidIpAddress:\n                return \"invalid IP address\"\n            case .invalidPrefixLength:\n                return \"invalid prefix length\"\n            case .unexpectedInfo(let type):\n                return \"unexpected response information, type = \\(type)\"\n            case .unexpectedOffset(let offset, let size):\n                return \"unexpected buffer state, offset = \\(offset), size = \\(size)\"\n            case .unexpectedResidualPackets:\n                return \"unexpected residual response packets\"\n            case .unexpectedResultSet(let count, let expected):\n                return \"unexpected result set size, count = \\(count), expected = \\(expected)\"\n            }\n        }\n    }\n\n    /// Performs a link set command on an interface.\n    /// - Parameters:\n    ///   - interface: The name of the interface.\n    ///   - up: The value to set the interface state to.\n    public func linkSet(interface: String, up: Bool, mtu: UInt32? = nil) throws {\n        // ip link set dev [interface] [up|down]\n        let interfaceIndex = try getInterfaceIndex(interface)\n        // build the attribute only when mtu is supplied\n        let attr: RTAttribute? =\n            (mtu != nil)\n            ? RTAttribute(\n                len: UInt16(RTAttribute.size + MemoryLayout<UInt32>.size),\n                type: LinkAttributeType.IFLA_MTU)\n            : nil\n        let requestSize = NetlinkMessageHeader.size + InterfaceInfo.size + (attr?.paddedLen ?? 0)\n        var requestBuffer = [UInt8](repeating: 0, count: requestSize)\n        var requestOffset = 0\n\n        let requestHeader = NetlinkMessageHeader(\n            len: UInt32(requestBuffer.count),\n            type: NetlinkType.RTM_NEWLINK,\n            flags: NetlinkFlags.NLM_F_REQUEST | NetlinkFlags.NLM_F_ACK,\n            pid: socket.pid)\n        requestOffset = try requestHeader.appendBuffer(&requestBuffer, offset: requestOffset)\n\n        let flags = up ? InterfaceFlags.IFF_UP : 0\n        let requestInfo = InterfaceInfo(\n            family: UInt8(AddressFamily.AF_PACKET),\n            index: interfaceIndex,\n            flags: flags,\n            change: InterfaceFlags.DEFAULT_CHANGE)\n        requestOffset = try requestInfo.appendBuffer(&requestBuffer, offset: requestOffset)\n\n        if let attr = attr, let m = mtu {\n            requestOffset = try attr.appendBuffer(&requestBuffer, offset: requestOffset)\n            guard\n                let newRequestOffset =\n                    requestBuffer.copyIn(as: UInt32.self, value: m, offset: requestOffset)\n            else {\n                throw BindError.sendMarshalFailure(type: \"RTAttribute\", field: \"IFLA_MTU\")\n            }\n            requestOffset = newRequestOffset\n        }\n\n        guard requestOffset == requestSize else {\n            throw Error.unexpectedOffset(offset: requestOffset, size: requestSize)\n        }\n\n        try sendRequest(buffer: &requestBuffer)\n        let (infos, _) = try parseResponse(infoType: NetlinkType.RTM_NEWLINK) { InterfaceInfo() }\n        guard infos.count == 0 else {\n            throw Error.unexpectedResultSet(count: infos.count, expected: 0)\n        }\n    }\n\n    /// Performs a link get command on an interface.\n    /// Returns information about the interface.\n    /// - Parameter interface: The name of the interface to query.\n    public func linkGet(interface: String? = nil, includeStats: Bool = false) throws -> [LinkResponse] {\n        // ip link ip show\n        let maskAttr = RTAttribute(\n            len: UInt16(RTAttribute.size + MemoryLayout<UInt32>.size), type: LinkAttributeType.IFLA_EXT_MASK)\n        let interfaceName = try interface.map { try getInterfaceName($0) }\n        let interfaceNameAttr = interfaceName.map {\n            RTAttribute(len: UInt16(RTAttribute.size + $0.count), type: LinkAttributeType.IFLA_IFNAME)\n        }\n        let requestSize =\n            NetlinkMessageHeader.size + InterfaceInfo.size + maskAttr.paddedLen + (interfaceNameAttr?.paddedLen ?? 0)\n        var requestBuffer = [UInt8](repeating: 0, count: requestSize)\n        var requestOffset = 0\n\n        let flags =\n            interface != nil ? NetlinkFlags.NLM_F_REQUEST : (NetlinkFlags.NLM_F_REQUEST | NetlinkFlags.NLM_F_DUMP)\n        let requestHeader = NetlinkMessageHeader(\n            len: UInt32(requestBuffer.count),\n            type: NetlinkType.RTM_GETLINK,\n            flags: flags,\n            pid: socket.pid)\n        requestOffset = try requestHeader.appendBuffer(&requestBuffer, offset: requestOffset)\n\n        let requestInfo = InterfaceInfo(\n            family: UInt8(AddressFamily.AF_PACKET),\n            index: 0,\n            flags: InterfaceFlags.IFF_UP,\n            change: InterfaceFlags.DEFAULT_CHANGE)\n        requestOffset = try requestInfo.appendBuffer(&requestBuffer, offset: requestOffset)\n\n        var filters = LinkAttributeMaskFilter.RTEXT_FILTER_VF\n        if !includeStats {\n            filters |= LinkAttributeMaskFilter.RTEXT_FILTER_SKIP_STATS\n        }\n\n        requestOffset = try maskAttr.appendBuffer(&requestBuffer, offset: requestOffset)\n        guard\n            var requestOffset = requestBuffer.copyIn(\n                as: UInt32.self,\n                value: filters,\n                offset: requestOffset)\n        else {\n            throw BindError.sendMarshalFailure(type: \"RTAttribute\", field: \"IFLA_EXT_MASK\")\n        }\n\n        if let interfaceNameAttr {\n            if let interfaceName {\n                requestOffset = try interfaceNameAttr.appendBuffer(&requestBuffer, offset: requestOffset)\n                guard let updatedRequestOffset = requestBuffer.copyIn(buffer: interfaceName, offset: requestOffset)\n                else {\n                    throw BindError.sendMarshalFailure(type: \"RTAttribute\", field: \"IFLA_IFNAME\")\n                }\n                requestOffset = updatedRequestOffset\n            }\n        }\n\n        guard requestOffset == requestSize else {\n            throw Error.unexpectedOffset(offset: requestOffset, size: requestSize)\n        }\n\n        try sendRequest(buffer: &requestBuffer)\n        let (infos, attrDataLists) = try parseResponse(infoType: NetlinkType.RTM_NEWLINK) { InterfaceInfo() }\n        var linkResponses: [LinkResponse] = []\n        for i in 0..<infos.count {\n            linkResponses.append(\n                LinkResponse(\n                    interfaceIndex: infos[i].index,\n                    interfaceFlags: infos[i].flags,\n                    interfaceType: infos[i].type,\n                    attrDatas: attrDataLists[i])\n            )\n        }\n\n        return linkResponses\n    }\n\n    /// Adds an IPv4 address to an interface.\n    /// - Parameters:\n    ///   - interface: The name of the interface.\n    ///   - ipv4Address: The CIDRv4 address describing the interface IP and subnet prefix length.\n    public func addressAdd(interface: String, ipv4Address: CIDRv4) throws {\n        // ip addr add [addr] dev [interface]\n        // ip address {add|change|replace} IFADDR dev IFNAME [ LIFETIME ] [ CONFFLAG-LIST ]\n        // IFADDR := PREFIX | ADDR peer PREFIX\n        //           [ broadcast ADDR ] [ anycast ADDR ]\n        //           [ label IFNAME ] [ scope SCOPE-ID ] [ metric METRIC ]\n        // SCOPE-ID := [ host | link | global | NUMBER ]\n        // CONFFLAG-LIST := [ CONFFLAG-LIST ] CONFFLAG\n        // CONFFLAG  := [ home | nodad | mngtmpaddr | noprefixroute | autojoin ]\n        // LIFETIME := [ valid_lft LFT ] [ preferred_lft LFT ]\n        // LFT := forever | SECONDS\n        let interfaceIndex = try getInterfaceIndex(interface)\n\n        let ipAddressBytes = ipv4Address.address.bytes\n        let addressAttrSize = RTAttribute.size + MemoryLayout<UInt8>.size * ipAddressBytes.count\n        let requestSize = NetlinkMessageHeader.size + AddressInfo.size + 2 * addressAttrSize\n        var requestBuffer = [UInt8](repeating: 0, count: requestSize)\n        var requestOffset = 0\n\n        let header = NetlinkMessageHeader(\n            len: UInt32(requestBuffer.count),\n            type: NetlinkType.RTM_NEWADDR,\n            flags: NetlinkFlags.NLM_F_REQUEST | NetlinkFlags.NLM_F_ACK | NetlinkFlags.NLM_F_EXCL\n                | NetlinkFlags.NLM_F_CREATE,\n            seq: 0,\n            pid: socket.pid)\n        requestOffset = try header.appendBuffer(&requestBuffer, offset: requestOffset)\n\n        let requestInfo = AddressInfo(\n            family: UInt8(AddressFamily.AF_INET),\n            prefixLength: ipv4Address.prefix.length,\n            flags: 0,\n            scope: NetlinkScope.RT_SCOPE_UNIVERSE,\n            index: UInt32(interfaceIndex))\n        requestOffset = try requestInfo.appendBuffer(&requestBuffer, offset: requestOffset)\n\n        let ipLocalAttr = RTAttribute(len: UInt16(addressAttrSize), type: AddressAttributeType.IFA_LOCAL)\n        requestOffset = try ipLocalAttr.appendBuffer(&requestBuffer, offset: requestOffset)\n        guard var requestOffset = requestBuffer.copyIn(buffer: ipAddressBytes, offset: requestOffset) else {\n            throw BindError.sendMarshalFailure(type: \"RTAttribute\", field: \"IFA_LOCAL\")\n        }\n\n        let ipAddressAttr = RTAttribute(len: UInt16(addressAttrSize), type: AddressAttributeType.IFA_ADDRESS)\n        requestOffset = try ipAddressAttr.appendBuffer(&requestBuffer, offset: requestOffset)\n        guard let requestOffset = requestBuffer.copyIn(buffer: ipAddressBytes, offset: requestOffset) else {\n            throw BindError.sendMarshalFailure(type: \"RTAttribute\", field: \"IFA_ADDRESS\")\n        }\n\n        guard requestOffset == requestSize else {\n            throw Error.unexpectedOffset(offset: requestOffset, size: requestSize)\n        }\n\n        try sendRequest(buffer: &requestBuffer)\n        let (infos, _) = try parseResponse(infoType: NetlinkType.RTM_NEWLINK) { AddressInfo() }\n        guard infos.count == 0 else {\n            throw Error.unexpectedResultSet(count: infos.count, expected: 0)\n        }\n    }\n\n    /// Adds an IPv4 route to an interface.\n    /// - Parameters:\n    ///   - interface: The name of the interface.\n    ///   - dstIpv4Addr: The CIDRv4 address describing the gateway IP and subnet prefix length.\n    ///   - srcIpv4Addr: The source IPv4 address to route from.\n    public func routeAdd(\n        interface: String,\n        dstIpv4Addr: CIDRv4,\n        srcIpv4Addr: IPv4Address?\n    ) throws {\n        // ip route add [dest-cidr] dev [interface] [src [src-addr]] proto kernel\n        let interfaceIndex = try getInterfaceIndex(interface)\n\n        let dstAddrBytes = dstIpv4Addr.address.bytes\n        let dstAddrAttrSize = RTAttribute.size + dstAddrBytes.count\n        let srcAddrAttrSize: Int\n        if let srcIpv4Addr {\n            let srcAddrBytes = srcIpv4Addr.bytes\n            srcAddrAttrSize = RTAttribute.size + srcAddrBytes.count\n        } else {\n            srcAddrAttrSize = 0\n        }\n        let interfaceAttrSize = RTAttribute.size + MemoryLayout<UInt32>.size\n        let requestSize =\n            NetlinkMessageHeader.size + RouteInfo.size + dstAddrAttrSize + srcAddrAttrSize + interfaceAttrSize\n        var requestBuffer = [UInt8](repeating: 0, count: requestSize)\n        var requestOffset = 0\n\n        let header = NetlinkMessageHeader(\n            len: UInt32(requestBuffer.count),\n            type: NetlinkType.RTM_NEWROUTE,\n            flags: NetlinkFlags.NLM_F_REQUEST | NetlinkFlags.NLM_F_ACK | NetlinkFlags.NLM_F_EXCL\n                | NetlinkFlags.NLM_F_CREATE,\n            pid: socket.pid)\n        requestOffset = try header.appendBuffer(&requestBuffer, offset: requestOffset)\n\n        let requestInfo = RouteInfo(\n            family: UInt8(AddressFamily.AF_INET),\n            dstLen: dstIpv4Addr.prefix.length,\n            srcLen: 0,\n            tos: 0,\n            table: RouteTable.MAIN,\n            proto: RouteProtocol.KERNEL,\n            scope: RouteScope.LINK,\n            type: RouteType.UNICAST,\n            flags: 0)\n        requestOffset = try requestInfo.appendBuffer(&requestBuffer, offset: requestOffset)\n\n        let dstAddrAttr = RTAttribute(len: UInt16(dstAddrAttrSize), type: RouteAttributeType.DST)\n        requestOffset = try dstAddrAttr.appendBuffer(&requestBuffer, offset: requestOffset)\n        guard var requestOffset = requestBuffer.copyIn(buffer: dstAddrBytes, offset: requestOffset) else {\n            throw BindError.sendMarshalFailure(type: \"RTAttribute\", field: \"RTA_DST\")\n        }\n\n        if let srcIpv4Addr {\n            let srcAddrBytes = srcIpv4Addr.bytes\n            let srcAddrAttr = RTAttribute(len: UInt16(srcAddrAttrSize), type: RouteAttributeType.PREFSRC)\n            requestOffset = try srcAddrAttr.appendBuffer(&requestBuffer, offset: requestOffset)\n            guard let newOffset = requestBuffer.copyIn(buffer: srcAddrBytes, offset: requestOffset) else {\n                throw BindError.sendMarshalFailure(type: \"RTAttribute\", field: \"RTA_PREFSRC\")\n            }\n            requestOffset = newOffset\n        }\n\n        let interfaceAttr = RTAttribute(len: UInt16(interfaceAttrSize), type: RouteAttributeType.OIF)\n        requestOffset = try interfaceAttr.appendBuffer(&requestBuffer, offset: requestOffset)\n        guard\n            let requestOffset = requestBuffer.copyIn(\n                as: UInt32.self,\n                value: UInt32(interfaceIndex),\n                offset: requestOffset)\n        else {\n            throw BindError.sendMarshalFailure(type: \"RTAttribute\", field: \"RTA_OIF\")\n        }\n\n        guard requestOffset == requestSize else {\n            throw Error.unexpectedOffset(offset: requestOffset, size: requestSize)\n        }\n\n        try sendRequest(buffer: &requestBuffer)\n        let (infos, _) = try parseResponse(infoType: NetlinkType.RTM_NEWLINK) { AddressInfo() }\n        guard infos.count == 0 else {\n            throw Error.unexpectedResultSet(count: infos.count, expected: 0)\n        }\n    }\n\n    /// Adds a default IPv4 route to an interface.\n    /// - Parameters:\n    ///   - interface: The name of the interface.\n    ///   - ipv4Gateway: The gateway address.\n    public func routeAddDefault(\n        interface: String,\n        ipv4Gateway: IPv4Address\n    ) throws {\n        // ip route add default via [dst-address] src [src-address]\n        let dstAddrBytes = ipv4Gateway.bytes\n        let dstAddrAttrSize = RTAttribute.size + dstAddrBytes.count\n\n        let interfaceAttrSize = RTAttribute.size + MemoryLayout<UInt32>.size\n        let interfaceIndex = try getInterfaceIndex(interface)\n        let requestSize = NetlinkMessageHeader.size + RouteInfo.size + dstAddrAttrSize + interfaceAttrSize\n\n        var requestBuffer = [UInt8](repeating: 0, count: requestSize)\n        var requestOffset = 0\n\n        let header = NetlinkMessageHeader(\n            len: UInt32(requestBuffer.count),\n            type: NetlinkType.RTM_NEWROUTE,\n            flags: NetlinkFlags.NLM_F_REQUEST | NetlinkFlags.NLM_F_ACK | NetlinkFlags.NLM_F_EXCL\n                | NetlinkFlags.NLM_F_CREATE,\n            pid: socket.pid)\n        requestOffset = try header.appendBuffer(&requestBuffer, offset: requestOffset)\n\n        let requestInfo = RouteInfo(\n            family: UInt8(AddressFamily.AF_INET),\n            dstLen: 0,\n            srcLen: 0,\n            tos: 0,\n            table: RouteTable.MAIN,\n            proto: RouteProtocol.BOOT,\n            scope: RouteScope.UNIVERSE,\n            type: RouteType.UNICAST,\n            flags: 0)\n        requestOffset = try requestInfo.appendBuffer(&requestBuffer, offset: requestOffset)\n\n        let dstAddrAttr = RTAttribute(len: UInt16(dstAddrAttrSize), type: RouteAttributeType.GATEWAY)\n        requestOffset = try dstAddrAttr.appendBuffer(&requestBuffer, offset: requestOffset)\n        guard var requestOffset = requestBuffer.copyIn(buffer: dstAddrBytes, offset: requestOffset) else {\n            throw BindError.sendMarshalFailure(type: \"RTAttribute\", field: \"RTA_GATEWAY\")\n        }\n        let interfaceAttr = RTAttribute(len: UInt16(interfaceAttrSize), type: RouteAttributeType.OIF)\n        requestOffset = try interfaceAttr.appendBuffer(&requestBuffer, offset: requestOffset)\n        guard\n            let requestOffset = requestBuffer.copyIn(\n                as: UInt32.self,\n                value: UInt32(interfaceIndex),\n                offset: requestOffset)\n        else {\n            throw BindError.sendMarshalFailure(type: \"RTAttribute\", field: \"RTA_OIF\")\n        }\n\n        guard requestOffset == requestSize else {\n            throw Error.unexpectedOffset(offset: requestOffset, size: requestSize)\n        }\n\n        try sendRequest(buffer: &requestBuffer)\n        let (infos, _) = try parseResponse(infoType: NetlinkType.RTM_NEWLINK) { AddressInfo() }\n        guard infos.count == 0 else {\n            throw Error.unexpectedResultSet(count: infos.count, expected: 0)\n        }\n    }\n\n    private func getInterfaceName(_ interface: String) throws -> [UInt8] {\n        guard let interfaceNameData = interface.data(using: .utf8) else {\n            throw BindError.sendMarshalFailure(type: \"String\", field: \"interface\")\n        }\n\n        var interfaceName = [UInt8](interfaceNameData)\n        interfaceName.append(0)\n\n        while interfaceName.count % MemoryLayout<UInt32>.size != 0 {\n            interfaceName.append(0)\n        }\n\n        return interfaceName\n    }\n\n    private func getInterfaceIndex(_ interface: String) throws -> Int32 {\n        let linkResponses = try linkGet(interface: interface)\n        guard linkResponses.count == 1 else {\n            throw Error.unexpectedResultSet(count: linkResponses.count, expected: 1)\n        }\n\n        return linkResponses[0].interfaceIndex\n    }\n\n    private func sendRequest(buffer: inout [UInt8]) throws {\n        log.trace(\"SEND-LENGTH: \\(buffer.count)\")\n        log.trace(\"SEND-DUMP: \\(buffer[0..<buffer.count].hexEncodedString())\")\n        let sendLength = try socket.send(buf: &buffer, len: buffer.count, flags: 0)\n        if sendLength != buffer.count {\n            log.warning(\"sent length \\(sendLength) not equal to packet length \\(buffer.count)\")\n        }\n    }\n\n    private func receiveResponse() throws -> ([UInt8], Int) {\n        var buffer = [UInt8](repeating: 0, count: Self.receiveDataLength)\n        let size = try socket.recv(buf: &buffer, len: Self.receiveDataLength, flags: 0)\n        log.trace(\"RECV-LENGTH: \\(size)\")\n        log.trace(\"RECV-DUMP: \\(buffer[0..<size].hexEncodedString())\")\n        return (buffer, size)\n    }\n\n    private func parseResponse<T: Bindable>(infoType: UInt16? = nil, _ infoProvider: () -> T) throws -> (\n        [T], [[RTAttributeData]]\n    ) {\n        var infos: [T] = []\n        var attrDataLists: [[RTAttributeData]] = []\n\n        var moreResponses = false\n        repeat {\n            var (buffer, size) = try receiveResponse()\n            var offset = 0\n\n            // A single buffer may contain multiple netlink messages\n            while offset < size {\n                let messageStart = offset\n                let header: NetlinkMessageHeader\n                (header, offset) = try parseHeader(buffer: &buffer, offset: offset)\n\n                if let infoType {\n                    if header.type == infoType {\n                        log.trace(\n                            \"RECV-INFO-DUMP:  dump = \\(buffer[offset..<offset + InterfaceInfo.size].hexEncodedString())\")\n                        var info = infoProvider()\n                        offset = try info.bindBuffer(&buffer, offset: offset)\n                        log.trace(\"RECV-INFO: \\(info)\")\n\n                        // Calculate the number of bytes remaining in THIS message\n                        let messageEnd = messageStart + Int(header.len)\n                        let attributeBytes = messageEnd - offset\n\n                        let attrDatas: [RTAttributeData]\n                        (attrDatas, offset) = try parseAttributes(\n                            buffer: &buffer,\n                            offset: offset,\n                            residualCount: attributeBytes)\n\n                        infos.append(info)\n                        attrDataLists.append(attrDatas)\n                    } else {\n                        // Skip this message - advance offset to the end of the message\n                        offset = messageStart + Int(header.len)\n                    }\n                } else if header.type != NetlinkType.NLMSG_DONE && header.type != NetlinkType.NLMSG_ERROR\n                    && header.type != NetlinkType.NLMSG_NOOP\n                {\n                    throw Error.unexpectedInfo(type: header.type)\n                }\n\n                moreResponses = header.moreResponses\n            }\n\n            guard offset == size else {\n                throw Error.unexpectedOffset(offset: offset, size: size)\n            }\n        } while moreResponses\n\n        return (infos, attrDataLists)\n    }\n\n    private func parseErrorCode(buffer: inout [UInt8], offset: Int) throws -> (Int32, Int) {\n        guard let errorPtr = buffer.bind(as: Int32.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"NetlinkErrorMessage\", field: \"error\")\n        }\n\n        let rc = errorPtr.pointee\n        log.trace(\"RECV-ERR-CODE: \\(rc)\")\n\n        return (rc, offset + MemoryLayout<Int32>.size)\n    }\n\n    private func parseErrorResponse(buffer: inout [UInt8], offset: Int) throws -> Int {\n        var (rc, offset) = try parseErrorCode(buffer: &buffer, offset: offset)\n        log.trace(\n            \"RECV-ERR-HEADER-DUMP:  dump = \\(buffer[offset..<offset + NetlinkMessageHeader.size].hexEncodedString())\")\n        var header = NetlinkMessageHeader()\n        offset = try header.bindBuffer(&buffer, offset: offset)\n        log.trace(\"RECV-ERR-HEADER: \\(header))\")\n\n        guard rc == 0 else {\n            throw NetlinkDataError.responseError(rc: rc)\n        }\n\n        return offset\n    }\n\n    private func parseHeader(buffer: inout [UInt8], offset: Int) throws -> (NetlinkMessageHeader, Int) {\n        log.trace(\"RECV-HEADER-DUMP:  dump = \\(buffer[offset..<offset + NetlinkMessageHeader.size].hexEncodedString())\")\n        var header = NetlinkMessageHeader()\n        var offset = try header.bindBuffer(&buffer, offset: offset)\n        log.trace(\"RECV-HEADER: \\(header)\")\n        switch header.type {\n        case NetlinkType.NLMSG_ERROR:\n            offset = try parseErrorResponse(buffer: &buffer, offset: offset)\n        case NetlinkType.NLMSG_DONE:\n            let rc: Int32\n            (rc, offset) = try parseErrorCode(buffer: &buffer, offset: offset)\n            guard rc == 0 else {\n                throw NetlinkDataError.responseError(rc: rc)\n            }\n        default:\n            break\n        }\n        return (header, offset)\n    }\n\n    private func parseAttributes(buffer: inout [UInt8], offset: Int, residualCount: Int) throws -> (\n        [RTAttributeData], Int\n    ) {\n        var attrDatas: [RTAttributeData] = []\n        var offset = offset\n        var residualCount = residualCount\n        log.trace(\"RECV-RESIDUAL: \\(residualCount)\")\n\n        while residualCount > 0 {\n            var attr = RTAttribute()\n            log.trace(\"  RECV-ATTR-DUMP: dump = \\(buffer[offset..<offset + RTAttribute.size].hexEncodedString())\")\n            offset = try attr.bindBuffer(&buffer, offset: offset)\n            log.trace(\"  RECV-ATTR: len = \\(attr.len) type = \\(attr.type)\")\n            let dataLen = Int(attr.len) - RTAttribute.size\n            if dataLen >= 0 {\n                log.trace(\"  RECV-ATTR-DATA-DUMP: dump = \\(buffer[offset..<offset + dataLen].hexEncodedString())\")\n                attrDatas.append(RTAttributeData(attribute: attr, data: Array(buffer[offset..<offset + dataLen])))\n            } else {\n                attrDatas.append(RTAttributeData(attribute: attr, data: []))\n            }\n            residualCount -= Int(attr.paddedLen)\n            offset += attr.paddedLen - RTAttribute.size\n            log.trace(\"RECV-RESIDUAL: \\(residualCount)\")\n        }\n\n        return (attrDatas, offset)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationNetlink/NetlinkSocket.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// A protocol for interacting with a netlink socket.\npublic protocol NetlinkSocket {\n    var pid: UInt32 { get }\n    func send(buf: UnsafeRawPointer!, len: Int, flags: Int32) throws -> Int\n    func recv(buf: UnsafeMutableRawPointer!, len: Int, flags: Int32) throws -> Int\n}\n\n/// A netlink socket provider.\npublic typealias NetlinkSocketProvider = () throws -> any NetlinkSocket\n\n/// Errors thrown when interacting with a netlink socket.\npublic enum NetlinkSocketError: Swift.Error, CustomStringConvertible, Equatable {\n    case socketFailure(rc: Int32)\n    case bindFailure(rc: Int32)\n    case sendFailure(rc: Int32)\n    case recvFailure(rc: Int32)\n    case notImplemented\n\n    /// The description of the errors.\n    public var description: String {\n        switch self {\n        case .socketFailure(let rc):\n            return \"could not create netlink socket, rc = \\(rc)\"\n        case .bindFailure(let rc):\n            return \"could not bind netlink socket, rc = \\(rc)\"\n        case .sendFailure(let rc):\n            return \"could not send netlink packet, rc = \\(rc)\"\n        case .recvFailure(let rc):\n            return \"could not receive netlink packet, rc = \\(rc)\"\n        case .notImplemented:\n            return \"socket function not implemented for platform\"\n        }\n    }\n}\n\n#if canImport(Musl)\nimport Musl\nlet osSocket = Musl.socket\nlet osBind = Musl.bind\nlet osSend = Musl.send\nlet osRecv = Musl.recv\n\n/// A default implementation of `NetlinkSocket`.\npublic class DefaultNetlinkSocket: NetlinkSocket {\n    private let sockfd: Int32\n\n    /// The process identifier of the process creating this socket.\n    public let pid: UInt32\n\n    /// Creates a new instance.\n    public init() throws {\n        pid = UInt32(getpid())\n        sockfd = osSocket(Int32(AddressFamily.AF_NETLINK), SocketType.SOCK_RAW, NetlinkProtocol.NETLINK_ROUTE)\n        guard sockfd >= 0 else {\n            throw NetlinkSocketError.socketFailure(rc: errno)\n        }\n\n        let addr = SockaddrNetlink(family: AddressFamily.AF_NETLINK, pid: pid)\n        var buffer = [UInt8](repeating: 0, count: SockaddrNetlink.size)\n        _ = try addr.appendBuffer(&buffer, offset: 0)\n        guard let ptr = buffer.bind(as: sockaddr.self, size: buffer.count) else {\n            throw NetlinkSocketError.bindFailure(rc: 0)\n        }\n        guard osBind(sockfd, ptr, UInt32(buffer.count)) >= 0 else {\n            throw NetlinkSocketError.bindFailure(rc: errno)\n        }\n    }\n\n    deinit {\n        close(sockfd)\n    }\n\n    /// Sends a request to a netlink socket.\n    /// Returns the number of bytes sent.\n    /// - Parameters:\n    ///   - buf: The buffer to send.\n    ///   - len: The length of the buffer to send.\n    ///   - flags: The send flags.\n    public func send(buf: UnsafeRawPointer!, len: Int, flags: Int32) throws -> Int {\n        let count = osSend(sockfd, buf, len, flags)\n        guard count >= 0 else {\n            throw NetlinkSocketError.sendFailure(rc: errno)\n        }\n\n        return count\n    }\n\n    /// Receives a response from a netlink socket.\n    /// Returns the number of bytes received.\n    /// - Parameters:\n    ///   - buf: The buffer to receive into.\n    ///   - len: The maximum number of bytes to receive.\n    ///   - flags: The receive flags.\n    public func recv(buf: UnsafeMutableRawPointer!, len: Int, flags: Int32) throws -> Int {\n        let count = osRecv(sockfd, buf, len, flags)\n        guard count >= 0 else {\n            throw NetlinkSocketError.recvFailure(rc: errno)\n        }\n\n        return count\n    }\n}\n#else\npublic class DefaultNetlinkSocket: NetlinkSocket {\n    public var pid: UInt32 { 0 }\n\n    public init() throws {}\n\n    public func send(buf: UnsafeRawPointer!, len: Int, flags: Int32) throws -> Int {\n        throw NetlinkSocketError.notImplemented\n    }\n\n    public func recv(buf: UnsafeMutableRawPointer!, len: Int, flags: Int32) throws -> Int {\n        throw NetlinkSocketError.notImplemented\n    }\n}\n#endif\n"
  },
  {
    "path": "Sources/ContainerizationNetlink/Types.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationExtras\nimport Foundation\n\nstruct SocketType {\n    static let SOCK_RAW: Int32 = 3\n}\n\nstruct AddressFamily {\n    static let AF_UNSPEC: UInt16 = 0\n    static let AF_INET: UInt16 = 2\n    static let AF_INET6: UInt16 = 10\n    static let AF_NETLINK: UInt16 = 16\n    static let AF_PACKET: UInt16 = 17\n}\n\nstruct ArpHardware {\n    static let ARPHRD_ETHER: UInt16 = 1\n}\n\nstruct NetlinkProtocol {\n    static let NETLINK_ROUTE: Int32 = 0\n}\n\nstruct NetlinkType {\n    static let NLMSG_NOOP: UInt16 = 1\n    static let NLMSG_ERROR: UInt16 = 2\n    static let NLMSG_DONE: UInt16 = 3\n    static let NLMSG_OVERRUN: UInt16 = 4\n    static let RTM_NEWLINK: UInt16 = 16\n    static let RTM_DELLINK: UInt16 = 17\n    static let RTM_GETLINK: UInt16 = 18\n    static let RTM_NEWADDR: UInt16 = 20\n    static let RTM_NEWROUTE: UInt16 = 24\n}\n\nstruct NetlinkFlags {\n    static let NLM_F_REQUEST: UInt16 = 0x01\n    static let NLM_F_MULTI: UInt16 = 0x02\n    static let NLM_F_ACK: UInt16 = 0x04\n    static let NLM_F_ECHO: UInt16 = 0x08\n    static let NLM_F_DUMP_INTR: UInt16 = 0x10\n    static let NLM_F_DUMP_FILTERED: UInt16 = 0x20\n\n    // GET request\n    static let NLM_F_ROOT: UInt16 = 0x100\n    static let NLM_F_MATCH: UInt16 = 0x200\n    static let NLM_F_ATOMIC: UInt16 = 0x400\n    static let NLM_F_DUMP: UInt16 = NetlinkFlags.NLM_F_ROOT | NetlinkFlags.NLM_F_MATCH\n\n    // NEW request flags\n    static let NLM_F_REPLACE: UInt16 = 0x100\n    static let NLM_F_EXCL: UInt16 = 0x200\n    static let NLM_F_CREATE: UInt16 = 0x400\n    static let NLM_F_APPEND: UInt16 = 0x800\n}\n\nstruct NetlinkScope {\n    static let RT_SCOPE_UNIVERSE: UInt8 = 0\n}\n\nstruct InterfaceFlags {\n    static let IFF_UP: UInt32 = 1 << 0\n    static let IFF_LOOPBACK: UInt32 = 1 << 3\n    static let IFF_POINTOPOINT: UInt32 = 1 << 4\n    static let DEFAULT_CHANGE: UInt32 = 0xffff_ffff\n}\n\nstruct LinkAttributeType {\n    static let IFLA_ADDRESS: UInt16 = 1\n    static let IFLA_BROADCAST: UInt16 = 2\n    static let IFLA_IFNAME: UInt16 = 3\n    static let IFLA_MTU: UInt16 = 4\n    static let IFLA_STATS64: UInt16 = 23\n    static let IFLA_EXT_MASK: UInt16 = 29\n}\n\nstruct LinkAttributeMaskFilter {\n    static let RTEXT_FILTER_VF: UInt32 = 1 << 0\n    static let RTEXT_FILTER_SKIP_STATS: UInt32 = 1 << 3\n}\n\nstruct AddressAttributeType {\n    // subnet mask\n    static let IFA_ADDRESS: UInt16 = 1\n    // IPv4 address\n    static let IFA_LOCAL: UInt16 = 2\n}\n\nstruct RouteTable {\n    static let MAIN: UInt8 = 254\n}\n\nstruct RouteProtocol {\n    static let UNSPEC: UInt8 = 0\n    static let REDIRECT: UInt8 = 1\n    static let KERNEL: UInt8 = 2\n    static let BOOT: UInt8 = 3\n    static let STATIC: UInt8 = 4\n}\n\nstruct RouteScope {\n    static let UNIVERSE: UInt8 = 0\n    static let LINK: UInt8 = 253\n}\n\nstruct RouteType {\n    static let UNSPEC: UInt8 = 0\n    static let UNICAST: UInt8 = 1\n}\n\nstruct RouteAttributeType {\n    static let UNSPEC: UInt16 = 0\n    static let DST: UInt16 = 1\n    static let SRC: UInt16 = 2\n    static let IIF: UInt16 = 3\n    static let OIF: UInt16 = 4\n    static let GATEWAY: UInt16 = 5\n    static let PRIORITY: UInt16 = 6\n    static let PREFSRC: UInt16 = 7\n}\n\nstruct SockaddrNetlink: Bindable, Equatable {\n    static let size = 12\n\n    var family: UInt16\n    var _pad: UInt16 = 0\n    var pid: UInt32\n    var groups: UInt32\n\n    init(family: UInt16 = 0, pid: UInt32 = 0, groups: UInt32 = 0) {\n        self.family = family\n        self.pid = pid\n        self.groups = groups\n    }\n\n    func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {\n        guard let offset = buffer.copyIn(as: UInt16.self, value: family, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"SockaddrNetlink\", field: \"family\")\n        }\n        guard let offset = buffer.copyIn(as: UInt16.self, value: 0, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"SockaddrNetlink\", field: \"_pad\")\n        }\n        guard let offset = buffer.copyIn(as: UInt32.self, value: pid, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"SockaddrNetlink\", field: \"pid\")\n        }\n        guard let offset = buffer.copyIn(as: UInt32.self, value: groups, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"SockaddrNetlink\", field: \"groups\")\n        }\n\n        return offset\n    }\n\n    mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {\n        guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"SockaddrNetlink\", field: \"family\")\n        }\n        family = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"SockaddrNetlink\", field: \"_pad\")\n        }\n        _pad = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"SockaddrNetlink\", field: \"pid\")\n        }\n        pid = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"SockaddrNetlink\", field: \"groups\")\n        }\n        groups = value\n\n        return offset\n    }\n}\n\nstruct NetlinkMessageHeader: Bindable, Equatable {\n    static let size = 16\n\n    var len: UInt32\n    var type: UInt16\n    var flags: UInt16\n    var seq: UInt32\n    var pid: UInt32\n\n    init(len: UInt32 = 0, type: UInt16 = 0, flags: UInt16 = 0, seq: UInt32? = nil, pid: UInt32 = 0) {\n        self.len = len\n        self.type = type\n        self.flags = flags\n        self.seq = seq ?? UInt32.random(in: 0..<UInt32.max)\n        self.pid = pid\n    }\n\n    func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {\n        guard let offset = buffer.copyIn(as: UInt32.self, value: len, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"NetlinkMessageHeader\", field: \"len\")\n        }\n        guard let offset = buffer.copyIn(as: UInt16.self, value: type, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"NetlinkMessageHeader\", field: \"type\")\n        }\n        guard let offset = buffer.copyIn(as: UInt16.self, value: flags, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"NetlinkMessageHeader\", field: \"flags\")\n        }\n        guard let offset = buffer.copyIn(as: UInt32.self, value: seq, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"NetlinkMessageHeader\", field: \"seq\")\n        }\n        guard let offset = buffer.copyIn(as: UInt32.self, value: pid, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"NetlinkMessageHeader\", field: \"pid\")\n        }\n\n        return offset\n    }\n\n    mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {\n        guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"NetlinkMessageHeader\", field: \"len\")\n        }\n        len = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"NetlinkMessageHeader\", field: \"type\")\n        }\n        type = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"NetlinkMessageHeader\", field: \"flags\")\n        }\n        flags = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"NetlinkMessageHeader\", field: \"seq\")\n        }\n        seq = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"NetlinkMessageHeader\", field: \"pid\")\n        }\n        pid = value\n\n        return offset\n    }\n\n    var moreResponses: Bool {\n        (self.flags & NetlinkFlags.NLM_F_MULTI) != 0\n            && (self.type != NetlinkType.NLMSG_DONE && self.type != NetlinkType.NLMSG_ERROR\n                && self.type != NetlinkType.NLMSG_OVERRUN)\n    }\n}\n\nstruct InterfaceInfo: Bindable, Equatable {\n    static let size = 16\n\n    var family: UInt8\n    var _pad: UInt8 = 0\n    var type: UInt16\n    var index: Int32\n    var flags: UInt32\n    var change: UInt32\n\n    init(\n        family: UInt8 = UInt8(AddressFamily.AF_UNSPEC), type: UInt16 = 0, index: Int32 = 0, flags: UInt32 = 0,\n        change: UInt32 = 0\n    ) {\n        self.family = family\n        self.type = type\n        self.index = index\n        self.flags = flags\n        self.change = change\n    }\n\n    func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {\n        guard let offset = buffer.copyIn(as: UInt8.self, value: family, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"InterfaceInfo\", field: \"family\")\n        }\n        guard let offset = buffer.copyIn(as: UInt8.self, value: _pad, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"InterfaceInfo\", field: \"_pad\")\n        }\n        guard let offset = buffer.copyIn(as: UInt16.self, value: type, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"InterfaceInfo\", field: \"type\")\n        }\n        guard let offset = buffer.copyIn(as: Int32.self, value: index, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"InterfaceInfo\", field: \"index\")\n        }\n        guard let offset = buffer.copyIn(as: UInt32.self, value: flags, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"InterfaceInfo\", field: \"flags\")\n        }\n        guard let offset = buffer.copyIn(as: UInt32.self, value: change, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"InterfaceInfo\", field: \"change\")\n        }\n\n        return offset\n    }\n\n    mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {\n        guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"InterfaceInfo\", field: \"family\")\n        }\n        family = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"InterfaceInfo\", field: \"_pad\")\n        }\n        _pad = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"InterfaceInfo\", field: \"type\")\n        }\n        type = value\n\n        guard let (offset, value) = buffer.copyOut(as: Int32.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"InterfaceInfo\", field: \"index\")\n        }\n        index = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"InterfaceInfo\", field: \"flags\")\n        }\n        flags = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"InterfaceInfo\", field: \"change\")\n        }\n        change = value\n\n        return offset\n    }\n}\n\nstruct AddressInfo: Bindable, Equatable {\n    static let size = 8\n\n    var family: UInt8\n    var prefixLength: UInt8\n    var flags: UInt8\n    var scope: UInt8\n    var index: UInt32\n\n    init(\n        family: UInt8 = UInt8(AddressFamily.AF_UNSPEC), prefixLength: UInt8 = 32, flags: UInt8 = 0, scope: UInt8 = 0,\n        index: UInt32 = 0\n    ) {\n        self.family = family\n        self.prefixLength = prefixLength\n        self.flags = flags\n        self.scope = scope\n        self.index = index\n    }\n\n    func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {\n        guard let offset = buffer.copyIn(as: UInt8.self, value: family, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"AddressInfo\", field: \"family\")\n        }\n        guard let offset = buffer.copyIn(as: UInt8.self, value: prefixLength, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"AddressInfo\", field: \"prefixLength\")\n        }\n        guard let offset = buffer.copyIn(as: UInt8.self, value: flags, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"AddressInfo\", field: \"flags\")\n        }\n        guard let offset = buffer.copyIn(as: UInt8.self, value: scope, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"AddressInfo\", field: \"scope\")\n        }\n        guard let offset = buffer.copyIn(as: UInt32.self, value: index, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"AddressInfo\", field: \"index\")\n        }\n\n        return offset\n    }\n\n    mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {\n        guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"AddressInfo\", field: \"family\")\n        }\n        family = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"AddressInfo\", field: \"prefixLength\")\n        }\n        prefixLength = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"AddressInfo\", field: \"flags\")\n        }\n        flags = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"AddressInfo\", field: \"scope\")\n        }\n        scope = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"AddressInfo\", field: \"index\")\n        }\n        index = value\n\n        return offset\n    }\n}\n\nstruct RouteInfo: Bindable, Equatable {\n    static let size = 12\n\n    var family: UInt8\n    var dstLen: UInt8\n    var srcLen: UInt8\n    var tos: UInt8\n    var table: UInt8\n    var proto: UInt8\n    var scope: UInt8\n    var type: UInt8\n    var flags: UInt32\n\n    init(\n        family: UInt8 = UInt8(AddressFamily.AF_INET),\n        dstLen: UInt8,\n        srcLen: UInt8,\n        tos: UInt8,\n        table: UInt8,\n        proto: UInt8,\n        scope: UInt8,\n        type: UInt8,\n        flags: UInt32\n    ) {\n        self.family = family\n        self.dstLen = dstLen\n        self.srcLen = srcLen\n        self.tos = tos\n        self.table = table\n        self.proto = proto\n        self.scope = scope\n        self.type = type\n        self.flags = flags\n    }\n\n    func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {\n        guard let offset = buffer.copyIn(as: UInt8.self, value: family, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"RouteInfo\", field: \"family\")\n        }\n        guard let offset = buffer.copyIn(as: UInt8.self, value: dstLen, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"RouteInfo\", field: \"dstLen\")\n        }\n        guard let offset = buffer.copyIn(as: UInt8.self, value: srcLen, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"RouteInfo\", field: \"srcLen\")\n        }\n        guard let offset = buffer.copyIn(as: UInt8.self, value: tos, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"RouteInfo\", field: \"tos\")\n        }\n        guard let offset = buffer.copyIn(as: UInt8.self, value: table, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"RouteInfo\", field: \"table\")\n        }\n        guard let offset = buffer.copyIn(as: UInt8.self, value: proto, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"RouteInfo\", field: \"proto\")\n        }\n        guard let offset = buffer.copyIn(as: UInt8.self, value: scope, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"RouteInfo\", field: \"scope\")\n        }\n        guard let offset = buffer.copyIn(as: UInt8.self, value: type, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"RouteInfo\", field: \"type\")\n        }\n        guard let offset = buffer.copyIn(as: UInt32.self, value: flags, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"RouteInfo\", field: \"flags\")\n        }\n\n        return offset\n    }\n\n    mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {\n        guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"RouteInfo\", field: \"family\")\n        }\n        family = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"RouteInfo\", field: \"dstLen\")\n        }\n        dstLen = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"RouteInfo\", field: \"srcLen\")\n        }\n        srcLen = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"RouteInfo\", field: \"tos\")\n        }\n        tos = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"RouteInfo\", field: \"table\")\n        }\n        table = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"RouteInfo\", field: \"proto\")\n        }\n        proto = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"RouteInfo\", field: \"scope\")\n        }\n        scope = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"RouteInfo\", field: \"type\")\n        }\n        type = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"RouteInfo\", field: \"flags\")\n        }\n        flags = value\n\n        return offset\n    }\n}\n\n/// A route information.\npublic struct RTAttribute: Bindable, Equatable {\n    package static let size = 4\n\n    public var len: UInt16\n    public var type: UInt16\n    public var paddedLen: Int { Int(((len + 3) >> 2) << 2) }\n\n    init(len: UInt16 = 0, type: UInt16 = 0) {\n        self.len = len\n        self.type = type\n    }\n\n    package func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {\n        guard let offset = buffer.copyIn(as: UInt16.self, value: len, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"RTAttribute\", field: \"len\")\n        }\n        guard let offset = buffer.copyIn(as: UInt16.self, value: type, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"RTAttribute\", field: \"type\")\n        }\n\n        return offset\n    }\n\n    package mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {\n        guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"RTAttribute\", field: \"len\")\n        }\n        len = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"RTAttribute\", field: \"type\")\n        }\n        type = value\n\n        return offset\n    }\n}\n\n/// A route information with data.\npublic struct RTAttributeData {\n    public let attribute: RTAttribute\n    public let data: [UInt8]\n}\n\n/// A response from the get link command.\npublic struct LinkResponse {\n    public let interfaceIndex: Int32\n    public let interfaceFlags: UInt32\n    public let interfaceType: UInt16\n    public let attrDatas: [RTAttributeData]\n\n    public var isLoopback: Bool {\n        (interfaceFlags & InterfaceFlags.IFF_LOOPBACK) != 0\n    }\n\n    public var isEthernet: Bool {\n        interfaceType == ArpHardware.ARPHRD_ETHER\n            && (interfaceFlags & (InterfaceFlags.IFF_LOOPBACK | InterfaceFlags.IFF_POINTOPOINT)) == 0\n    }\n\n    public var address: [UInt8]? {\n        attrDatas\n            .filter { $0.attribute.type == LinkAttributeType.IFLA_ADDRESS }\n            .first\n            .map { $0.data }\n    }\n\n    /// Extract network interface statistics from the response attributes\n    public func getStatistics() throws -> LinkStatistics64? {\n        for attrData in attrDatas {\n            if attrData.attribute.type == LinkAttributeType.IFLA_STATS64 {\n                var stats = LinkStatistics64()\n                var buffer = attrData.data\n                _ = try stats.bindBuffer(&buffer, offset: 0)\n                return stats\n            }\n        }\n\n        return nil\n    }\n}\n\n/// Network interface statistics (64-bit version)\npublic struct LinkStatistics64: Bindable, Equatable {\n    package static let size = 23 * 8\n\n    public var rxPackets: UInt64\n    public var txPackets: UInt64\n    public var rxBytes: UInt64\n    public var txBytes: UInt64\n    public var rxErrors: UInt64\n    public var txErrors: UInt64\n    public var rxDropped: UInt64\n    public var txDropped: UInt64\n    public var multicast: UInt64\n    public var collisions: UInt64\n    public var rxLengthErrors: UInt64\n    public var rxOverErrors: UInt64\n    public var rxCrcErrors: UInt64\n    public var rxFrameErrors: UInt64\n    public var rxFifoErrors: UInt64\n    public var rxMissedErrors: UInt64\n    public var txAbortedErrors: UInt64\n    public var txCarrierErrors: UInt64\n    public var txFifoErrors: UInt64\n    public var txHeartbeatErrors: UInt64\n    public var txWindowErrors: UInt64\n    public var rxCompressed: UInt64\n    public var txCompressed: UInt64\n\n    public init() {\n        self.rxPackets = 0\n        self.txPackets = 0\n        self.rxBytes = 0\n        self.txBytes = 0\n        self.rxErrors = 0\n        self.txErrors = 0\n        self.rxDropped = 0\n        self.txDropped = 0\n        self.multicast = 0\n        self.collisions = 0\n        self.rxLengthErrors = 0\n        self.rxOverErrors = 0\n        self.rxCrcErrors = 0\n        self.rxFrameErrors = 0\n        self.rxFifoErrors = 0\n        self.rxMissedErrors = 0\n        self.txAbortedErrors = 0\n        self.txCarrierErrors = 0\n        self.txFifoErrors = 0\n        self.txHeartbeatErrors = 0\n        self.txWindowErrors = 0\n        self.rxCompressed = 0\n        self.txCompressed = 0\n    }\n\n    package func appendBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {\n        guard let offset = buffer.copyIn(as: UInt64.self, value: rxPackets, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"rxPackets\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: txPackets, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"txPackets\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: rxBytes, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"rxBytes\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: txBytes, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"txBytes\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: rxErrors, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"rxErrors\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: txErrors, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"txErrors\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: rxDropped, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"rxDropped\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: txDropped, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"txDropped\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: multicast, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"multicast\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: collisions, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"collisions\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: rxLengthErrors, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"rxLengthErrors\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: rxOverErrors, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"rxOverErrors\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: rxCrcErrors, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"rxCrcErrors\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: rxFrameErrors, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"rxFrameErrors\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: rxFifoErrors, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"rxFifoErrors\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: rxMissedErrors, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"rxMissedErrors\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: txAbortedErrors, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"txAbortedErrors\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: txCarrierErrors, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"txCarrierErrors\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: txFifoErrors, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"txFifoErrors\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: txHeartbeatErrors, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"txHeartbeatErrors\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: txWindowErrors, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"txWindowErrors\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: rxCompressed, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"rxCompressed\")\n        }\n        guard let offset = buffer.copyIn(as: UInt64.self, value: txCompressed, offset: offset) else {\n            throw BindError.sendMarshalFailure(type: \"LinkStatistics64\", field: \"txCompressed\")\n        }\n\n        return offset\n    }\n\n    package mutating func bindBuffer(_ buffer: inout [UInt8], offset: Int) throws -> Int {\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"rxPackets\")\n        }\n        rxPackets = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"txPackets\")\n        }\n        txPackets = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"rxBytes\")\n        }\n        rxBytes = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"txBytes\")\n        }\n        txBytes = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"rxErrors\")\n        }\n        rxErrors = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"txErrors\")\n        }\n        txErrors = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"rxDropped\")\n        }\n        rxDropped = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"txDropped\")\n        }\n        txDropped = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"multicast\")\n        }\n        multicast = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"collisions\")\n        }\n        collisions = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"rxLengthErrors\")\n        }\n        rxLengthErrors = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"rxOverErrors\")\n        }\n        rxOverErrors = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"rxCrcErrors\")\n        }\n        rxCrcErrors = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"rxFrameErrors\")\n        }\n        rxFrameErrors = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"rxFifoErrors\")\n        }\n        rxFifoErrors = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"rxMissedErrors\")\n        }\n        rxMissedErrors = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"txAbortedErrors\")\n        }\n        txAbortedErrors = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"txCarrierErrors\")\n        }\n        txCarrierErrors = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"txFifoErrors\")\n        }\n        txFifoErrors = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"txHeartbeatErrors\")\n        }\n        txHeartbeatErrors = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"txWindowErrors\")\n        }\n        txWindowErrors = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"rxCompressed\")\n        }\n        rxCompressed = value\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: offset) else {\n            throw BindError.recvMarshalFailure(type: \"LinkStatistics64\", field: \"txCompressed\")\n        }\n        txCompressed = value\n\n        return offset\n    }\n}\n\n/// Errors thrown when parsing netlink data.\npublic enum NetlinkDataError: Swift.Error, CustomStringConvertible, Equatable {\n    case responseError(rc: Int32)\n    case unsupportedPlatform\n\n    /// The description of the errors.\n    public var description: String {\n        switch self {\n        case .responseError(let rc):\n            return \"netlink response indicates error, rc = \\(rc)\"\n        case .unsupportedPlatform:\n            return \"unsupported platform\"\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/AnnotationKeys.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// AnnotationKeys contains a subset of \"dictionary keys\" for commonly used annotations in an OCI Image Descriptor\n/// https://github.com/opencontainers/image-spec/blob/main/annotations.md\npublic struct AnnotationKeys: Codable, Sendable {\n    public static let containerizationIndexIndirect = \"com.apple.containerization.index.indirect\"\n    public static let containerizationImageName = \"com.apple.containerization.image.name\"\n    public static let containerdImageName = \"io.containerd.image.name\"\n    public static let openContainersImageName = \"org.opencontainers.image.ref.name\"\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Bundle.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport Foundation\n\n#if canImport(Musl)\nimport Musl\nprivate let _mount = Musl.mount\nprivate let _umount = Musl.umount2\n#elseif canImport(Glibc)\nimport Glibc\nprivate let _mount = Glibc.mount\nprivate let _umount = Glibc.umount2\n#endif\n\n/// `Bundle` represents an OCI runtime spec bundle for running\n/// a container.\npublic struct Bundle: Sendable {\n    /// The path to the bundle.\n    public let path: URL\n\n    /// The path to the OCI runtime spec config.json file.\n    public var configPath: URL {\n        self.path.appending(path: \"config.json\")\n    }\n\n    /// The path to a rootfs mount inside the bundle.\n    public var rootfsPath: URL {\n        self.path.appending(path: \"rootfs\")\n    }\n\n    /// Create the OCI bundle.\n    ///\n    /// - Parameters:\n    ///   - path: A URL pointing to where to create the bundle on the filesystem.\n    ///   - spec: A data blob that should contain an OCI runtime spec. This will be written\n    ///           to the bundle as a \"config.json\" file.\n    public static func create(path: URL, spec: Data) throws -> Bundle {\n        try self.init(path: path, spec: spec)\n    }\n\n    /// Create the OCI bundle.\n    ///\n    /// - Parameters:\n    ///   - path: A URL pointing to where to create the bundle on the filesystem.\n    ///   - spec: An OCI runtime spec that will be written to the bundle as a \"config.json\"\n    ///           file.\n    public static func create(path: URL, spec: ContainerizationOCI.Spec) throws -> Bundle {\n        try self.init(path: path, spec: spec)\n    }\n\n    /// Load an OCI bundle from the provided path.\n    ///\n    /// - Parameters:\n    ///   - path: A URL pointing to where to load the bundle from on the filesystem.\n    public static func load(path: URL) throws -> Bundle {\n        try self.init(path: path)\n    }\n\n    private init(path: URL) throws {\n        let fm = FileManager.default\n        if !fm.fileExists(atPath: path.path) {\n            throw ContainerizationError(.invalidArgument, message: \"no bundle at \\(path.path)\")\n        }\n        self.path = path\n    }\n\n    // This constructor does not do any validation that data is actually a\n    // valid OCI spec.\n    private init(path: URL, spec: Data) throws {\n        self.path = path\n\n        let fm = FileManager.default\n        try fm.createDirectory(\n            atPath: self.path.appending(component: \"rootfs\").path,\n            withIntermediateDirectories: true\n        )\n\n        try spec.write(to: self.configPath)\n    }\n\n    private init(path: URL, spec: ContainerizationOCI.Spec) throws {\n        self.path = path\n\n        let fm = FileManager.default\n        try fm.createDirectory(\n            atPath: self.path.appending(component: \"rootfs\").path,\n            withIntermediateDirectories: true\n        )\n\n        let specData = try JSONEncoder().encode(spec)\n        try specData.write(to: self.configPath)\n    }\n\n    /// Delete the OCI bundle from the filesystem.\n    public func delete() throws {\n        // Unmount, and then blow away the dir.\n        #if os(Linux)\n        let rootfs = self.rootfsPath\n        if Self.isMountpoint(rootfs) {\n            guard _umount(rootfs.path, 0) == 0 else {\n                throw POSIXError.fromErrno()\n            }\n        }\n        #endif\n        // removeItem is recursive so should blow away the rootfs dir inside as well.\n        let fm = FileManager.default\n        try fm.removeItem(at: self.path)\n    }\n\n    /// Load and return the OCI runtime spec written to the bundle.\n    public func loadConfig() throws -> ContainerizationOCI.Spec {\n        let data = try Data(contentsOf: self.configPath)\n        return try JSONDecoder().decode(ContainerizationOCI.Spec.self, from: data)\n    }\n\n    private static func isMountpoint(_ path: URL) -> Bool {\n        var st = stat()\n        var parent_st = stat()\n\n        guard stat(path.path, &st) == 0 else {\n            return false\n        }\n\n        let parentPath = path.deletingLastPathComponent()\n        guard stat(parentPath.path, &parent_st) == 0 else {\n            return false\n        }\n\n        return st.st_dev != parent_st.st_dev\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Client/Authentication.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// Abstraction for returning a token needed for logging into an OCI compliant registry.\npublic protocol Authentication: Sendable {\n    func token() async throws -> String\n}\n\n/// Type representing authentication information for client to access the registry.\npublic struct BasicAuthentication: Authentication {\n    /// The username for the authentication.\n    let username: String\n    /// The password or identity token for the user.\n    let password: String\n\n    public init(username: String, password: String) {\n        self.username = username\n        self.password = password\n    }\n\n    /// Get a token using the provided username and password. This will be a\n    /// base64 encoded string of the username and password delimited by a colon.\n    public func token() async throws -> String {\n        let credentials = \"\\(username):\\(password)\"\n        if let authenticationData = credentials.data(using: .utf8)?.base64EncodedString() {\n            return \"Basic \\(authenticationData)\"\n        }\n        throw Error.invalidCredentials\n    }\n\n    /// `BasicAuthentication` errors.\n    public enum Error: Swift.Error {\n        case invalidCredentials\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Client/KeychainHelper.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(macOS)\nimport Foundation\nimport ContainerizationOS\n\n/// Helper type to lookup registry related values in the macOS keychain.\npublic struct KeychainHelper: Sendable {\n    private let securityDomain: String\n    private let accessGroup: String?\n\n    /// Create a new keychain helper.\n    /// - Parameters:\n    ///   - securityDomain: The security domain used to fetch registry entries in the keychain.\n    ///   - accessGroup: If present, the access group used to fetch registry entries in the keychain.\n    public init(securityDomain: String, accessGroup: String? = nil) {\n        self.securityDomain = securityDomain\n        self.accessGroup = accessGroup\n    }\n\n    /// Lookup authentication data for a given registry hostname.\n    /// - Parameters:\n    ///   - hostname: The hostname for the registry.\n    /// - Returns: The authentication object for the registry.\n    /// - Throws: An error if the keychain query fails.\n    public func lookup(hostname: String) throws -> Authentication {\n        let kq = KeychainQuery()\n\n        do {\n            guard\n                let fetched = try kq.get(\n                    securityDomain: self.securityDomain,\n                    accessGroup: self.accessGroup,\n                    hostname: hostname)\n            else {\n                throw Self.Error.keyNotFound\n            }\n            return BasicAuthentication(\n                username: fetched.username,\n                password: fetched.password\n            )\n        } catch let err as KeychainQuery.Error {\n            switch err {\n            case .keyNotPresent(_):\n                throw Self.Error.keyNotFound\n            default:\n                throw Self.Error.queryError(\"query failure: \\(String(describing: err))\")\n            }\n        }\n    }\n\n    /// Lists all registry entries for this security domain.\n    /// - Returns: An array of registry metadata for each matching entry, or an empty array if none are found.\n    /// - Throws: An error if the keychain query fails.\n    public func list() throws -> [RegistryInfo] {\n        let kq = KeychainQuery()\n        return try kq.list(securityDomain: self.securityDomain, accessGroup: self.accessGroup)\n    }\n\n    /// Delete authorization data for a given hostname from the keychain.\n    /// - Parameters:\n    ///   - hostname: The hostname for the registry.\n    /// - Throws: An error if the keychain query fails.\n    public func delete(hostname: String) throws {\n        let kq = KeychainQuery()\n        try kq.delete(securityDomain: self.securityDomain, accessGroup: self.accessGroup, hostname: hostname)\n    }\n\n    /// Save authorization data for a given hostname to the keychain.\n    /// - Parameters:\n    ///   - hostname: The hostname for the registry.\n    ///   - username: The username to present to the registry.\n    ///   - password: The password to present to the registry.\n    /// - Throws: An error if the keychain query fails or returns unexpected data.\n    public func save(hostname: String, username: String, password: String) throws {\n        let kq = KeychainQuery()\n        try kq.save(\n            securityDomain: self.securityDomain,\n            accessGroup: self.accessGroup,\n            hostname: hostname,\n            username: username,\n            password: password\n        )\n    }\n\n    /// Prompt for authorization data for a given hostname to be saved to the keychain.\n    /// This will cause the current terminal to enter a password prompt state where\n    /// key strokes are hidden.\n    public func credentialPrompt(hostname: String) throws -> Authentication {\n        let username = try userPrompt(hostname: hostname)\n        let password = try passwordPrompt()\n        return BasicAuthentication(username: username, password: password)\n    }\n\n    /// Prompts the current stdin for a username entry and then returns the value.\n    public func userPrompt(hostname: String) throws -> String {\n        print(\"Provide registry username \\(hostname): \", terminator: \"\")\n        guard let username = readLine() else {\n            throw Self.Error.invalidInput\n        }\n        return username\n    }\n\n    /// Prompts the current stdin for a password entry and then returns the value.\n    /// This will cause the current stdin (if it is a terminal) to hide keystrokes\n    /// by disabling echo.\n    public func passwordPrompt() throws -> String {\n        print(\"Provide registry password: \", terminator: \"\")\n        let console = try Terminal.current\n        defer { console.tryReset() }\n        try console.disableEcho()\n\n        guard let password = readLine() else {\n            throw Self.Error.invalidInput\n        }\n        return password\n    }\n}\n\nextension KeychainHelper {\n    /// `KeychainHelper` errors.\n    public enum Error: Swift.Error {\n        case keyNotFound\n        case invalidInput\n        case queryError(String)\n    }\n}\n#endif\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Client/LocalOCILayoutClient.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport ContainerizationExtras\nimport Crypto\nimport Foundation\nimport NIOCore\nimport NIOFoundationCompat\n\npackage final class LocalOCILayoutClient: ContentClient {\n    let cs: LocalContentStore\n\n    package init(root: URL) throws {\n        self.cs = try LocalContentStore(path: root)\n    }\n\n    private func _fetch(digest: String) async throws -> Content {\n        guard let c: Content = try await self.cs.get(digest: digest) else {\n            throw Error.missingContent(digest)\n        }\n        return c\n    }\n\n    private func calculateFileDigest(at url: URL) throws -> SHA256Digest {\n        let fileHandle = try FileHandle(forReadingFrom: url)\n        defer {\n            try? fileHandle.close()\n        }\n\n        var hasher = SHA256()\n        let chunkSize = Int(getpagesize()) * 1024\n\n        while true {\n            let chunk = fileHandle.readData(ofLength: chunkSize)\n            if chunk.isEmpty {\n                break\n            }\n            hasher.update(data: chunk)\n        }\n\n        return hasher.finalize()\n    }\n\n    package func fetch<T: Codable>(name: String, descriptor: Descriptor) async throws -> T {\n        let c = try await self._fetch(digest: descriptor.digest)\n        return try c.decode()\n    }\n\n    package func fetchBlob(name: String, descriptor: Descriptor, into file: URL, progress: ProgressHandler?) async throws -> (Int64, SHA256Digest) {\n        let c = try await self._fetch(digest: descriptor.digest)\n        let fileManager = FileManager.default\n        let filePath = file.absolutePath()\n\n        do {\n            let src = c.path\n            try fileManager.copyItem(at: src, to: file)\n\n            if let progress, let fileSize = fileManager.fileSize(atPath: filePath) {\n                await progress([\n                    .addSize(fileSize)\n                ])\n            }\n        } catch let error as NSError {\n            guard error.code == NSFileWriteFileExistsError else {\n                throw error\n            }\n\n            do {\n                let expectedDigest = try c.digest()\n                let existingDigest = try calculateFileDigest(at: file)\n\n                guard existingDigest.digestString == expectedDigest.digestString else {\n                    throw ContainerizationError(\n                        .internalError,\n                        message:\n                            \"file \\(filePath) exists but contains different content, expected digest: \\(expectedDigest.digestString), existing digest: \\(existingDigest.digestString)\"\n                    )\n                }\n\n                if let progress, let fileSize = fileManager.fileSize(atPath: filePath) {\n                    await progress([\n                        .addSize(fileSize)\n                    ])\n                }\n            } catch {\n                throw error\n            }\n        }\n\n        let size = try Int64(c.size())\n        let digest = try c.digest()\n        return (size, digest)\n    }\n\n    package func fetchData(name: String, descriptor: Descriptor) async throws -> Data {\n        let c = try await self._fetch(digest: descriptor.digest)\n        return try c.data()\n    }\n\n    package func push<T: Sendable & AsyncSequence>(\n        name: String,\n        ref: String,\n        descriptor: Descriptor,\n        streamGenerator: () throws -> T,\n        progress: ProgressHandler?\n    ) async throws where T.Element == ByteBuffer {\n        let input = try streamGenerator()\n\n        let (id, dir) = try await self.cs.newIngestSession()\n        do {\n            let into = dir.appendingPathComponent(descriptor.digest.trimmingDigestPrefix)\n            guard FileManager.default.createFile(atPath: into.path, contents: nil) else {\n                throw Error.cannotCreateFile\n            }\n            let fd = try FileHandle(forWritingTo: into)\n            defer {\n                try? fd.close()\n            }\n            var wrote = 0\n            var hasher = SHA256()\n\n            for try await buffer in input {\n                wrote += buffer.readableBytes\n                try fd.write(contentsOf: buffer.readableBytesView)\n                hasher.update(data: buffer.readableBytesView)\n            }\n            try await self.cs.completeIngestSession(id)\n        } catch {\n            try await self.cs.cancelIngestSession(id)\n        }\n    }\n}\n\nextension LocalOCILayoutClient {\n    private static let ociLayoutFileName = \"oci-layout\"\n    private static let ociLayoutVersionString = \"imageLayoutVersion\"\n    private static let ociLayoutIndexFileName = \"index.json\"\n\n    package func loadIndexFromOCILayout(directory: URL) throws -> ContainerizationOCI.Index {\n        let fm = FileManager.default\n        let decoder = JSONDecoder()\n\n        let ociLayoutFile = directory.appendingPathComponent(Self.ociLayoutFileName)\n        guard fm.fileExists(atPath: ociLayoutFile.absolutePath()) else {\n            throw ContainerizationError(.notFound, message: ociLayoutFile.absolutePath())\n        }\n        var data = try Data(contentsOf: ociLayoutFile)\n        let ociLayout = try decoder.decode([String: String].self, from: data)\n        guard ociLayout[Self.ociLayoutVersionString] != nil else {\n            throw ContainerizationError(.empty, message: \"missing key \\(Self.ociLayoutVersionString) in \\(ociLayoutFile.absolutePath())\")\n        }\n\n        let indexFile = directory.appendingPathComponent(Self.ociLayoutIndexFileName)\n        guard fm.fileExists(atPath: indexFile.absolutePath()) else {\n            throw ContainerizationError(.notFound, message: indexFile.absolutePath())\n        }\n        data = try Data(contentsOf: indexFile)\n        let index = try decoder.decode(ContainerizationOCI.Index.self, from: data)\n        return index\n    }\n\n    package func createOCILayoutStructure(directory: URL, manifests: [Descriptor]) throws {\n        let fm = FileManager.default\n        let encoder = JSONEncoder()\n        encoder.outputFormatting = [.withoutEscapingSlashes]\n\n        let ingestDir = directory.appendingPathComponent(\"ingest\")\n        try? fm.removeItem(at: ingestDir)\n        let ociLayoutContent: [String: String] = [\n            Self.ociLayoutVersionString: \"1.0.0\"\n        ]\n\n        var data = try encoder.encode(ociLayoutContent)\n        var p = directory.appendingPathComponent(Self.ociLayoutFileName).absolutePath()\n        guard fm.createFile(atPath: p, contents: data) else {\n            throw ContainerizationError(.internalError, message: \"failed to create file \\(p)\")\n        }\n        let idx = ContainerizationOCI.Index(schemaVersion: 2, manifests: manifests)\n        data = try encoder.encode(idx)\n        p = directory.appendingPathComponent(Self.ociLayoutIndexFileName).absolutePath()\n        guard fm.createFile(atPath: p, contents: data) else {\n            throw ContainerizationError(.internalError, message: \"failed to create file \\(p)\")\n        }\n    }\n\n    package func setImageReferenceAnnotation(descriptor: inout Descriptor, reference: String) {\n        var annotations = descriptor.annotations ?? [:]\n        annotations[AnnotationKeys.containerizationImageName] = reference\n        annotations[AnnotationKeys.containerdImageName] = reference\n        annotations[AnnotationKeys.openContainersImageName] = reference\n        descriptor.annotations = annotations\n    }\n\n    package func getImageReferencefromDescriptor(descriptor: Descriptor) -> String {\n        let annotations = descriptor.annotations\n\n        // Annotations here do not conform to the OCI image specification.\n        // The interpretation of the annotations \"org.opencontainers.image.ref.name\" and\n        // \"io.containerd.image.name\" is under debate:\n        //  - OCI spec examples suggest it should be the image tag:\n        //     https://github.com/opencontainers/image-spec/blob/fbb4662eb53b80bd38f7597406cf1211317768f0/image-layout.md?plain=1#L175\n        //  - Buildkitd maintainers argue it should represent the full image name:\n        //     https://github.com/moby/buildkit/issues/4615#issuecomment-2521810830\n        // Until a consensus is reached, the preference is given to \"com.apple.containerization.image.name\" and then to\n        // using \"io.containerd.image.name\" as it is the next safest choice\n        if let annotations {\n            if let name = annotations[AnnotationKeys.containerizationImageName] {\n                return name\n            }\n            if let name = annotations[AnnotationKeys.containerdImageName] {\n                return name\n            }\n            if let name = annotations[AnnotationKeys.openContainersImageName] {\n                return name\n            }\n        }\n\n        // Fallback: Generate digest-based reference for images without annotations\n        // This makes sure OCI spec compliance as annotations are optional\n        return \"untagged@\\(descriptor.digest)\"\n    }\n\n    package enum Error: Swift.Error {\n        case missingContent(_ digest: String)\n        case unsupportedInput\n        case cannotCreateFile\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Client/RegistryClient+Catalog.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport AsyncHTTPClient\nimport ContainerizationError\nimport Foundation\nimport NIOFoundationCompat\n\nprivate struct CatalogResponse: Sendable, Decodable {\n    let repositories: [String]\n}\n\nextension RegistryClient {\n    /// List repositories in the registry.\n    ///\n    /// Implements GET /v2/_catalog from the OCI Distribution Spec with pagination.\n    /// When prefix is provided, pagination skips ahead to the relevant portion of\n    /// the lexically-sorted catalog and stops once results move past the prefix.\n    ///\n    /// - Parameter prefix: Optional prefix to filter repository names. Must be at least\n    ///   two characters long to enable the skip-ahead optimization; shorter values are\n    ///   treated as no prefix.\n    /// - Returns: An array of repository names matching the prefix (or all repositories\n    ///   if no prefix is given).\n    public func catalog(prefix: String? = nil) async throws -> [String] {\n        let effectivePrefix = prefix.flatMap { $0.count >= 2 ? $0 : nil }\n\n        var allRepos: [String] = []\n        // When a prefix is provided, skip ahead in the lexically-sorted catalog\n        // by setting last to one position before the prefix. The OCI spec\n        // returns entries that sort after last, so dropping the last character\n        // of the prefix positions the cursor just before matching entries.\n        var last: String? = effectivePrefix.map { String($0.dropLast()) }\n        let pageSize = 100\n\n        while true {\n            var components = base\n            components.path = \"/v2/_catalog\"\n            var queryItems = [URLQueryItem(name: \"n\", value: String(pageSize))]\n            if let last {\n                queryItems.append(URLQueryItem(name: \"last\", value: last))\n            }\n            components.queryItems = queryItems\n\n            let repos: [String] = try await request(components: components) { response in\n                guard response.status == .ok else {\n                    let url = components.url?.absoluteString ?? \"unknown\"\n                    let reason = await ErrorResponse.fromResponseBody(response.body)?.jsonString\n                    throw Error.invalidStatus(url: url, response.status, reason: reason)\n                }\n\n                let buffer = try await response.body.collect(upTo: self.bufferSize)\n                return try JSONDecoder().decode(CatalogResponse.self, from: buffer).repositories\n            }\n\n            if let effectivePrefix {\n                let matching = repos.filter { $0.hasPrefix(effectivePrefix) }\n                allRepos.append(contentsOf: matching)\n                if let lastRepo = repos.last, !lastRepo.hasPrefix(effectivePrefix) && lastRepo > effectivePrefix {\n                    break\n                }\n            } else {\n                allRepos.append(contentsOf: repos)\n            }\n\n            if repos.count < pageSize { break }\n            last = repos.last\n        }\n\n        return allRepos\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Client/RegistryClient+Error.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport AsyncHTTPClient\nimport Foundation\nimport NIOHTTP1\n\nextension RegistryClient {\n    /// `RegistryClient` errors.\n    public enum Error: Swift.Error, CustomStringConvertible {\n        case invalidStatus(url: String, HTTPResponseStatus, reason: String? = nil)\n\n        /// Description of the errors.\n        public var description: String {\n            switch self {\n            case .invalidStatus(let u, let response, let reason):\n                return \"HTTP request to \\(u) failed with response: \\(response.description). Reason: \\(reason ?? \"Unknown\")\"\n            }\n        }\n    }\n\n    /// The container registry typically returns actionable failure reasons in the response body\n    /// of the failing HTTP Request. This type models the structure of the error message.\n    /// Reference: https://distribution.github.io/distribution/spec/api/#errors\n    internal struct ErrorResponse: Codable {\n        let errors: [RemoteError]\n\n        internal struct RemoteError: Codable {\n            let code: String\n            let message: String\n            let detail: String?\n        }\n\n        internal static func fromResponseBody(_ body: HTTPClientResponse.Body) async -> ErrorResponse? {\n            guard var buffer = try? await body.collect(upTo: Int(1.mib())) else {\n                return nil\n            }\n            guard let bytes = buffer.readBytes(length: buffer.readableBytes) else {\n                return nil\n            }\n            let data = Data(bytes)\n            guard let jsonError = try? JSONDecoder().decode(ErrorResponse.self, from: data) else {\n                return nil\n            }\n            return jsonError\n        }\n\n        public var jsonString: String {\n            let data = try? JSONEncoder().encode(self)\n            guard let data else {\n                return \"{}\"\n            }\n            return String(data: data, encoding: .utf8) ?? \"{}\"\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Client/RegistryClient+Fetch.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport AsyncHTTPClient\nimport ContainerizationError\nimport ContainerizationExtras\nimport Crypto\nimport Foundation\nimport NIOFoundationCompat\n\n#if os(macOS)\nimport _NIOFileSystem\n#endif\n\nextension RegistryClient {\n    /// Resolve sends a HEAD request to the registry to find root manifest descriptor.\n    /// This descriptor serves as an entry point to retrieve resources from the registry.\n    public func resolve(name: String, tag: String) async throws -> Descriptor {\n        var components = base\n\n        // Make HEAD request to retrieve the digest header\n        components.path = \"/v2/\\(name)/manifests/\\(tag)\"\n\n        // The client should include an Accept header indicating which manifest content types it supports.\n        let mediaTypes = [\n            MediaTypes.dockerManifest,\n            MediaTypes.dockerManifestList,\n            MediaTypes.imageManifest,\n            MediaTypes.index,\n            \"*/*\",\n        ]\n\n        let headers = [\n            (\"Accept\", mediaTypes.joined(separator: \", \"))\n        ]\n\n        return try await request(components: components, method: .HEAD, headers: headers) { response in\n            guard response.status == .ok else {\n                let url = components.url?.absoluteString ?? \"unknown\"\n                let reason = await ErrorResponse.fromResponseBody(response.body)?.jsonString\n                throw Error.invalidStatus(url: url, response.status, reason: reason)\n            }\n\n            guard let digest = response.headers.first(name: \"Docker-Content-Digest\") else {\n                throw ContainerizationError(.invalidArgument, message: \"missing required header Docker-Content-Digest\")\n            }\n\n            guard let type = response.headers.first(name: \"Content-Type\") else {\n                throw ContainerizationError(.invalidArgument, message: \"missing required header Content-Type\")\n            }\n\n            guard let sizeStr = response.headers.first(name: \"Content-Length\") else {\n                throw ContainerizationError(.invalidArgument, message: \"missing required header Content-Length\")\n            }\n\n            guard let size = Int64(sizeStr) else {\n                throw ContainerizationError(.invalidArgument, message: \"cannot convert \\(sizeStr) to Int64\")\n            }\n\n            return Descriptor(mediaType: type, digest: digest, size: size)\n        }\n    }\n\n    /// Fetch resource (either manifest or blob) to memory with JSON decoding.\n    public func fetch<T: Codable>(name: String, descriptor: Descriptor) async throws -> T {\n        var components = base\n\n        let manifestTypes = [\n            MediaTypes.dockerManifest,\n            MediaTypes.dockerManifestList,\n            MediaTypes.imageManifest,\n            MediaTypes.index,\n        ]\n\n        let isManifest = manifestTypes.contains(where: { $0 == descriptor.mediaType })\n        let resource = isManifest ? \"manifests\" : \"blobs\"\n\n        components.path = \"/v2/\\(name)/\\(resource)/\\(descriptor.digest)\"\n\n        let mediaType = descriptor.mediaType\n        if mediaType.isEmpty {\n            throw ContainerizationError(.invalidArgument, message: \"missing media type for descriptor \\(descriptor.digest)\")\n        }\n\n        let headers = [\n            (\"Accept\", mediaType)\n        ]\n\n        return try await requestJSON(components: components, headers: headers)\n    }\n\n    /// Fetch resource (either manifest or blob) to memory as raw `Data`.\n    public func fetchData(name: String, descriptor: Descriptor) async throws -> Data {\n        var components = base\n\n        let manifestTypes = [\n            MediaTypes.dockerManifest,\n            MediaTypes.dockerManifestList,\n            MediaTypes.imageManifest,\n            MediaTypes.index,\n        ]\n\n        let isManifest = manifestTypes.contains(where: { $0 == descriptor.mediaType })\n        let resource = isManifest ? \"manifests\" : \"blobs\"\n\n        components.path = \"/v2/\\(name)/\\(resource)/\\(descriptor.digest)\"\n\n        let mediaType = descriptor.mediaType\n        if mediaType.isEmpty {\n            throw ContainerizationError(.invalidArgument, message: \"missing media type for descriptor \\(descriptor.digest)\")\n        }\n\n        let headers = [\n            (\"Accept\", mediaType)\n        ]\n\n        return try await requestData(components: components, headers: headers)\n    }\n\n    /// Fetch a blob from remote registry.\n    /// This method is suitable for streaming data.\n    public func fetchBlob(\n        name: String,\n        descriptor: Descriptor,\n        closure: (Int64, HTTPClientResponse.Body) async throws -> Void\n    ) async throws {\n        var components = base\n        components.path = \"/v2/\\(name)/blobs/\\(descriptor.digest)\"\n\n        let mediaType = descriptor.mediaType\n        if mediaType.isEmpty {\n            throw ContainerizationError(.invalidArgument, message: \"missing media type for descriptor \\(descriptor.digest)\")\n        }\n\n        let headers = [\n            (\"Accept\", mediaType)\n        ]\n\n        try await request(components: components, headers: headers) { response in\n            guard response.status == .ok else {\n                let url = components.url?.absoluteString ?? \"unknown\"\n                let reason = await ErrorResponse.fromResponseBody(response.body)?.jsonString\n                throw Error.invalidStatus(url: url, response.status, reason: reason)\n            }\n\n            // How many bytes to expect\n            guard let expectedBytes = response.headers.first(name: \"Content-Length\").flatMap(Int64.init) else {\n                throw ContainerizationError(.invalidArgument, message: \"missing required header Content-Length\")\n            }\n\n            try await closure(expectedBytes, response.body)\n        }\n    }\n\n    #if os(macOS)\n    /// Fetch a blob from remote registry and write the contents into a file in the provided directory.\n    public func fetchBlob(name: String, descriptor: Descriptor, into file: URL, progress: ProgressHandler?) async throws -> (Int64, SHA256Digest) {\n        var hasher = SHA256()\n        var received: Int64 = 0\n        let fs = _NIOFileSystem.FileSystem.shared\n        let handle = try await fs.openFile(forWritingAt: FilePath(file.absolutePath()), options: .newFile(replaceExisting: true))\n        var writer = handle.bufferedWriter()\n        do {\n            try await self.fetchBlob(name: name, descriptor: descriptor) { (size, body) in\n                var itr = body.makeAsyncIterator()\n                while let buf = try await itr.next() {\n                    let readBytes = Int64(buf.readableBytes)\n                    received += readBytes\n                    let written = try await writer.write(contentsOf: buf)\n                    await progress?([\n                        .addSize(written)\n                    ])\n                    guard written == readBytes else {\n                        throw ContainerizationError(\n                            .internalError,\n                            message: \"could not write \\(readBytes) bytes to file \\(file)\"\n                        )\n                    }\n                    hasher.update(data: buf.readableBytesView)\n                }\n            }\n            try await writer.flush()\n            try await handle.close()\n        } catch {\n            do {\n                try await handle.close()\n            } catch {\n                // Use `detachUnsafeFileDescriptor()` as suggested by the error message to prevent a leak detection crash when `close()` fails.\n                _ = try handle.detachUnsafeFileDescriptor()\n            }\n            throw error\n        }\n        let computedDigest = hasher.finalize()\n        return (received, computedDigest)\n    }\n    #else\n    /// Fetch a blob from remote registry and write the contents into a file in the provided directory.\n    public func fetchBlob(name: String, descriptor: Descriptor, into file: URL, progress: ProgressHandler?) async throws -> (Int64, SHA256Digest) {\n        var hasher = SHA256()\n        var received: Int64 = 0\n        guard FileManager.default.createFile(atPath: file.path, contents: nil) else {\n            throw ContainerizationError(.internalError, message: \"cannot create file at path \\(file.path)\")\n        }\n        try await self.fetchBlob(name: name, descriptor: descriptor) { (size, body) in\n            let fd = try FileHandle(forWritingTo: file)\n            defer {\n                try? fd.close()\n            }\n            var itr = body.makeAsyncIterator()\n            while let buf = try await itr.next() {\n                let readBytes = Int64(buf.readableBytes)\n                received += readBytes\n                await progress?([\n                    .addSize(readBytes)\n                ])\n                try fd.write(contentsOf: buf.readableBytesView)\n                hasher.update(data: buf.readableBytesView)\n            }\n        }\n        let computedDigest = hasher.finalize()\n        return (received, computedDigest)\n    }\n    #endif\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Client/RegistryClient+Push.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport AsyncHTTPClient\nimport ContainerizationError\nimport ContainerizationExtras\nimport Foundation\nimport NIO\n\nextension RegistryClient {\n    /// Pushes the content specified by a descriptor to a remote registry.\n    /// - Parameters:\n    ///    - name:          The namespace which the descriptor should belong under.\n    ///    - tag:           The tag or digest for uniquely identifying the manifest.\n    ///                     By convention, any portion that may be a partial or whole digest\n    ///                     will be proceeded by an `@`. Anything preceding the `@` will be referred\n    ///                     to as \"tag\".\n    ///                     This is usually broken down into the following possibilities:\n    ///                         1. <tag>\n    ///                         2. <tag>@<digest>\n    ///                         3. @<digest>\n    ///                     The tag is anything except `@` and `:`, and digest is anything after the `@`\n    ///    - descriptor:    The OCI descriptor of the content to be pushed.\n    ///    - streamGenerator: A closure that produces an`AsyncStream` of `ByteBuffer`\n    ///                     for streaming data to the `HTTPClientRequest.Body`.\n    ///                     The caller is responsible for providing the `AsyncStream` where the data may come from\n    ///                     a file on disk, data in memory, etc.\n    ///    - progress: The progress handler to invoke as data is sent.\n    public func push<T: Sendable & AsyncSequence>(\n        name: String,\n        ref tag: String,\n        descriptor: Descriptor,\n        streamGenerator: () throws -> T,\n        progress: ProgressHandler?\n    ) async throws where T.Element == ByteBuffer {\n        var components = base\n\n        let mediaType = descriptor.mediaType\n        if mediaType.isEmpty {\n            throw ContainerizationError(.invalidArgument, message: \"missing media type for descriptor \\(descriptor.digest)\")\n        }\n\n        var isManifest = false\n        var existCheck: [String] = []\n\n        switch mediaType {\n        case MediaTypes.dockerManifest, MediaTypes.dockerManifestList, MediaTypes.imageManifest, MediaTypes.index:\n            isManifest = true\n            existCheck = self.getManifestPath(tag: tag, digest: descriptor.digest)\n        default:\n            existCheck = [\"blobs\", descriptor.digest]\n        }\n\n        // Check if the content already exists.\n        components.path = \"/v2/\\(name)/\\(existCheck.joined(separator: \"/\"))\"\n\n        let mediaTypes = [\n            mediaType,\n            \"*/*\",\n        ]\n\n        var headers = [\n            (\"Accept\", mediaTypes.joined(separator: \", \"))\n        ]\n\n        try await request(components: components, method: .HEAD, headers: headers) { response in\n            if response.status == .ok {\n                var exists = false\n                if isManifest && existCheck[1] != descriptor.digest {\n                    if descriptor.digest == response.headers.first(name: \"Docker-Content-Digest\") {\n                        exists = true\n                    }\n                } else {\n                    exists = true\n                }\n\n                if exists {\n                    throw ContainerizationError(.exists, message: \"content already exists \\(descriptor.digest)\")\n                }\n            } else if response.status != .notFound {\n                let url = components.url?.absoluteString ?? \"unknown\"\n                let reason = await ErrorResponse.fromResponseBody(response.body)?.jsonString\n                throw Error.invalidStatus(url: url, response.status, reason: reason)\n            }\n        }\n\n        if isManifest {\n            let path = self.getManifestPath(tag: tag, digest: descriptor.digest)\n            components.path = \"/v2/\\(name)/\\(path.joined(separator: \"/\"))\"\n            headers = [\n                (\"Content-Type\", mediaType)\n            ]\n        } else {\n            // Start upload request for blobs.\n            components.path = \"/v2/\\(name)/blobs/uploads/\"\n            try await request(components: components, method: .POST) { response in\n                switch response.status {\n                case .ok, .accepted, .noContent:\n                    break\n                case .created:\n                    throw ContainerizationError(.exists, message: \"content already exists \\(descriptor.digest)\")\n                default:\n                    let url = components.url?.absoluteString ?? \"unknown\"\n                    let reason = await ErrorResponse.fromResponseBody(response.body)?.jsonString\n                    throw Error.invalidStatus(url: url, response.status, reason: reason)\n                }\n\n                // Get the location to upload the blob.\n                guard let location = response.headers.first(name: \"Location\") else {\n                    throw ContainerizationError(.invalidArgument, message: \"missing required header Location\")\n                }\n\n                guard let urlComponents = URLComponents(string: location) else {\n                    throw ContainerizationError(.invalidArgument, message: \"invalid url \\(location)\")\n                }\n                var queryItems = urlComponents.queryItems ?? []\n                queryItems.append(URLQueryItem(name: \"digest\", value: descriptor.digest))\n                components.path = urlComponents.path\n                components.queryItems = queryItems\n                headers = [\n                    (\"Content-Type\", \"application/octet-stream\"),\n                    (\"Content-Length\", String(descriptor.size)),\n                ]\n            }\n        }\n\n        // We have to pass a body closure rather than a body to reset the stream when retrying.\n        let bodyClosure = {\n            let stream = try streamGenerator()\n            let body = HTTPClientRequest.Body.stream(stream, length: .known(descriptor.size))\n            return body\n        }\n\n        return try await request(components: components, method: .PUT, bodyClosure: bodyClosure, headers: headers) { response in\n            switch response.status {\n            case .ok, .created, .noContent:\n                break\n            default:\n                let url = components.url?.absoluteString ?? \"unknown\"\n                let reason = await ErrorResponse.fromResponseBody(response.body)?.jsonString\n                throw Error.invalidStatus(url: url, response.status, reason: reason)\n            }\n\n            guard descriptor.digest == response.headers.first(name: \"Docker-Content-Digest\") else {\n                let required = response.headers.first(name: \"Docker-Content-Digest\") ?? \"\"\n                throw ContainerizationError(.internalError, message: \"digest mismatch \\(descriptor.digest) != \\(required)\")\n            }\n        }\n    }\n\n    private func getManifestPath(tag: String, digest: String) -> [String] {\n        var object = tag\n        if let i = tag.firstIndex(of: \"@\") {\n            let index = tag.index(after: i)\n            if String(tag[index...]) != digest {\n                object = \"\"\n            } else {\n                object = String(tag[...i])\n            }\n        }\n\n        if object == \"\" {\n            return [\"manifests\", digest]\n        }\n\n        return [\"manifests\", object]\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Client/RegistryClient+Referrers.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport AsyncHTTPClient\nimport ContainerizationError\nimport Foundation\nimport NIOFoundationCompat\n\nextension RegistryClient {\n    /// Query the OCI referrers API for artifacts that reference a given manifest digest.\n    ///\n    /// Implements `GET /v2/{name}/referrers/{digest}` from the OCI Distribution Spec v1.1.\n    /// Falls back to the referrers tag schema when the API is not available (404).\n    ///\n    /// - Parameters:\n    ///   - name: The repository name (e.g., \"library/ubuntu\").\n    ///   - digest: The digest of the subject manifest (e.g., \"sha256:abc123...\").\n    ///   - artifactType: Optional filter to return only referrers with a matching artifactType.\n    /// - Returns: An `Index` whose `manifests` array contains descriptors of referring artifacts.\n    ///            Returns an empty index if the registry does not support the referrers API\n    ///            and no tag schema fallback is available.\n    public func referrers(name: String, digest: String, artifactType: String? = nil) async throws -> Index {\n        var components = base\n        components.path = \"/v2/\\(name)/referrers/\\(digest)\"\n\n        if let artifactType {\n            components.queryItems = [URLQueryItem(name: \"artifactType\", value: artifactType)]\n        }\n\n        let headers = [(\"Accept\", MediaTypes.index)]\n\n        let result: Index = try await request(components: components, method: .GET, headers: headers) { response in\n            if response.status == .notFound {\n                return await self.referrersTagFallback(name: name, digest: digest, artifactType: artifactType)\n            }\n\n            guard response.status == .ok else {\n                let url = components.url?.absoluteString ?? \"unknown\"\n                let reason = await ErrorResponse.fromResponseBody(response.body)?.jsonString\n                throw Error.invalidStatus(url: url, response.status, reason: reason)\n            }\n\n            let buffer = try await response.body.collect(upTo: self.bufferSize)\n            return try JSONDecoder().decode(Index.self, from: buffer)\n        }\n\n        return result\n    }\n\n    /// Fallback for registries that don't support the referrers API.\n    ///\n    /// Uses the OCI referrers tag schema: referrers for a digest are stored as an\n    /// index at the tag `<algorithm>-<hex>` (e.g., `sha256-abc123...`).\n    private func referrersTagFallback(name: String, digest: String, artifactType: String? = nil) async -> Index {\n        let referrerTag = digest.replacingOccurrences(of: \":\", with: \"-\")\n\n        let descriptor: Descriptor\n        do {\n            descriptor = try await resolve(name: name, tag: referrerTag)\n        } catch {\n            return Index(schemaVersion: 2, manifests: [])\n        }\n\n        let index: Index\n        do {\n            index = try await fetch(name: name, descriptor: descriptor)\n        } catch {\n            return Index(schemaVersion: 2, manifests: [])\n        }\n\n        guard let artifactType else {\n            return index\n        }\n\n        let filtered = index.manifests.filter { $0.artifactType == artifactType }\n        return Index(schemaVersion: 2, manifests: filtered)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Client/RegistryClient+Token.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport AsyncHTTPClient\nimport ContainerizationError\nimport Foundation\n\nstruct TokenRequest {\n    public static let authenticateHeaderName = \"WWW-Authenticate\"\n\n    /// The credentials that will be used in the authentication header when fetching the token.\n    let authentication: Authentication?\n    /// The realm against which the token should be requested.\n    let realm: String\n    /// The name of the service which hosts the resource.\n    let service: String\n    /// Whether to return a refresh token along with the bearer token.\n    let offlineToken: Bool\n    /// String identifying the client.\n    let clientId: String\n    /// The resource in question, formatted as one of the space-delimited entries from the scope parameters from the WWW-Authenticate header shown above.\n    let scope: String?\n\n    init(\n        realm: String,\n        service: String,\n        clientId: String,\n        scope: String?,\n        offlineToken: Bool = false,\n        authentication: Authentication? = nil\n    ) {\n        self.realm = realm\n        self.service = service\n        self.offlineToken = offlineToken\n        self.clientId = clientId\n        self.scope = scope\n        self.authentication = authentication\n    }\n}\n\nstruct TokenResponse: Codable, Hashable {\n    /// An opaque Bearer token that clients should supply to subsequent requests in the Authorization header.\n    let token: String?\n    /// For compatibility with OAuth 2.0, we will also accept token under the name access_token.\n    /// At least one of these fields must be specified, but both may also appear (for compatibility with older clients).\n    /// When both are specified, they should be equivalent; if they differ the client's choice is undefined.\n    let accessToken: String?\n    ///  The duration in seconds since the token was issued that it will remain valid.\n    ///  When omitted, this defaults to 60 seconds.\n    let expiresIn: UInt?\n    /// The RFC3339-serialized UTC standard time at which a given token was issued.\n    /// If issued_at is omitted, the expiration is from when the token exchange completed.\n    let issuedAt: String?\n    /// Token which can be used to get additional access tokens for the same subject with different scopes.\n    /// This token should be kept secure by the client and only sent to the authorization server which issues bearer tokens.\n    /// This field will only be set when `offline_token=true` is provided in the request.\n    let refreshToken: String?\n\n    var scope: String?\n\n    private enum CodingKeys: String, CodingKey {\n        case token = \"token\"\n        case accessToken = \"access_token\"\n        case expiresIn = \"expires_in\"\n        case issuedAt = \"issued_at\"\n        case refreshToken = \"refresh_token\"\n    }\n\n    func getToken() -> String? {\n        if let t = token ?? accessToken {\n            return \"Bearer \\(t)\"\n        }\n        return nil\n    }\n\n    func isValid(scope: String?) -> Bool {\n        guard let issuedAt else {\n            return false\n        }\n        let isoFormatter = ISO8601DateFormatter()\n        isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]\n        guard let issued = isoFormatter.date(from: issuedAt) else {\n            return false\n        }\n        let expiresIn = expiresIn ?? 0\n        let now = Date()\n        let elapsed = now.timeIntervalSince(issued)\n        guard elapsed < Double(expiresIn) else {\n            return false\n        }\n        if let requiredScope = scope {\n            return requiredScope == self.scope\n        }\n        return false\n    }\n}\n\nstruct AuthenticateChallenge: Equatable {\n    let type: String\n    let realm: String?\n    let service: String?\n    let scope: String?\n    let error: String?\n\n    init(type: String, realm: String?, service: String?, scope: String?, error: String?) {\n        self.type = type\n        self.realm = realm\n        self.service = service\n        self.scope = scope\n        self.error = error\n    }\n\n    init(type: String, values: [String: String]) {\n        self.type = type\n        self.realm = values[\"realm\"]\n        self.service = values[\"service\"]\n        self.scope = values[\"scope\"]\n        self.error = values[\"error\"]\n    }\n}\n\nextension RegistryClient {\n    /// Fetch an auto token for all subsequent HTTP requests\n    /// See https://docs.docker.com/registry/spec/auth/token/\n    internal func fetchToken(request: TokenRequest) async throws -> TokenResponse {\n        guard var components = URLComponents(string: request.realm) else {\n            throw ContainerizationError(.invalidArgument, message: \"cannot create URL from \\(request.realm)\")\n        }\n        components.queryItems = [\n            URLQueryItem(name: \"client_id\", value: request.clientId),\n            URLQueryItem(name: \"service\", value: request.service),\n        ]\n        var scope = \"\"\n        if let reqScope = request.scope {\n            scope = reqScope\n            components.queryItems?.append(URLQueryItem(name: \"scope\", value: reqScope))\n        }\n\n        if request.offlineToken {\n            components.queryItems?.append(URLQueryItem(name: \"offline_token\", value: \"true\"))\n        }\n        var response: TokenResponse = try await requestJSON(components: components, headers: [])\n        response.scope = scope\n        return response\n    }\n\n    internal func createTokenRequest(parsing authenticateHeaders: [String]) throws -> TokenRequest {\n        let parsedHeaders = Self.parseWWWAuthenticateHeaders(headers: authenticateHeaders)\n        let bearerChallenge = parsedHeaders.first { $0.type == \"Bearer\" }\n        guard let bearerChallenge else {\n            throw ContainerizationError(.invalidArgument, message: \"missing Bearer challenge in \\(TokenRequest.authenticateHeaderName) header\")\n        }\n        guard let realm = bearerChallenge.realm else {\n            throw ContainerizationError(.invalidArgument, message: \"cannot parse realm from \\(TokenRequest.authenticateHeaderName) header\")\n        }\n        guard let service = bearerChallenge.service else {\n            throw ContainerizationError(.invalidArgument, message: \"cannot parse service from \\(TokenRequest.authenticateHeaderName) header\")\n        }\n        let scope = bearerChallenge.scope\n        let tokenRequest = TokenRequest(realm: realm, service: service, clientId: self.clientID, scope: scope, authentication: self.authentication)\n        return tokenRequest\n    }\n\n    internal static func parseWWWAuthenticateHeaders(headers: [String]) -> [AuthenticateChallenge] {\n        var parsed: [String: [String: String]] = [:]\n        for challenge in headers {\n            let trimmedChallenge = challenge.trimmingCharacters(in: .whitespacesAndNewlines)\n            let parts = trimmedChallenge.split(separator: \" \", maxSplits: 1)\n            guard parts.count == 2 else {\n                continue\n            }\n            guard let scheme = parts.first else {\n                continue\n            }\n            var params: [String: String] = [:]\n            let header = String(parts[1])\n            let pattern = #\"(\\w+)=\"([^\"]+)\"#\n            let regex = try! NSRegularExpression(pattern: pattern, options: [])\n            let matches = regex.matches(in: header, options: [], range: NSRange(header.startIndex..., in: header))\n            for match in matches {\n                if let keyRange = Range(match.range(at: 1), in: header),\n                    let valueRange = Range(match.range(at: 2), in: header)\n                {\n                    let key = String(header[keyRange])\n                    let value = String(header[valueRange])\n                    params[key] = value\n                }\n            }\n            parsed[String(scheme)] = params\n        }\n        var parsedChallenges: [AuthenticateChallenge] = []\n        for (type, values) in parsed {\n            parsedChallenges.append(.init(type: type, values: values))\n        }\n        return parsedChallenges\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Client/RegistryClient.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport AsyncHTTPClient\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOS\nimport Foundation\nimport Logging\nimport NIO\nimport NIOHTTP1\nimport NIOSSL\n\n#if os(macOS)\nimport Network\n#endif\n\n/// Data used to control retry behavior for `RegistryClient`.\npublic struct RetryOptions: Sendable {\n    /// The maximum number of retries to attempt before failing.\n    public var maxRetries: Int\n    /// The retry interval in nanoseconds.\n    public var retryInterval: UInt64\n    /// A provided closure to handle if a given HTTP response should be\n    /// retried.\n    public var shouldRetry: (@Sendable (HTTPClientResponse) -> Bool)?\n\n    public init(maxRetries: Int, retryInterval: UInt64, shouldRetry: (@Sendable (HTTPClientResponse) -> Bool)? = nil) {\n        self.maxRetries = maxRetries\n        self.retryInterval = retryInterval\n        self.shouldRetry = shouldRetry\n    }\n}\n\n/// A client for interacting with OCI compliant container registries.\npublic final class RegistryClient: ContentClient {\n    private static let defaultRetryOptions = RetryOptions(\n        maxRetries: 3,\n        retryInterval: 1_000_000_000,\n        shouldRetry: ({ response in\n            response.status.code >= 500\n        })\n    )\n\n    let client: HTTPClient\n    let proxyURL: URL?\n    let base: URLComponents\n    let clientID: String\n    let authentication: Authentication?\n    let retryOptions: RetryOptions?\n    let bufferSize: Int\n\n    public convenience init(\n        reference: String,\n        insecure: Bool = false,\n        auth: Authentication? = nil,\n        tlsConfiguration: TLSConfiguration? = nil,\n        logger: Logger? = nil,\n    ) throws {\n        let ref = try Reference.parse(reference)\n        guard let domain = ref.resolvedDomain else {\n            throw ContainerizationError(.invalidArgument, message: \"invalid domain for image reference \\(reference)\")\n        }\n        let scheme = insecure ? \"http\" : \"https\"\n        let _url = \"\\(scheme)://\\(domain)\"\n        guard let url = URL(string: _url) else {\n            throw ContainerizationError(.invalidArgument, message: \"cannot convert \\(_url) to URL\")\n        }\n        guard let host = url.host else {\n            throw ContainerizationError(.invalidArgument, message: \"invalid host \\(domain)\")\n        }\n        let port = url.port\n        self.init(\n            host: host,\n            scheme: scheme,\n            port: port,\n            authentication: auth,\n            retryOptions: Self.defaultRetryOptions,\n            tlsConfiguration: tlsConfiguration,\n        )\n    }\n\n    public init(\n        host: String,\n        scheme: String? = \"https\",\n        port: Int? = nil,\n        authentication: Authentication? = nil,\n        clientID: String? = nil,\n        retryOptions: RetryOptions? = nil,\n        bufferSize: Int = Int(4.mib()),\n        tlsConfiguration: TLSConfiguration? = nil,\n        logger: Logger? = nil,\n    ) {\n        var components = URLComponents()\n        components.scheme = scheme\n        components.host = host\n        components.port = port\n\n        self.base = components\n        self.clientID = clientID ?? \"containerization-registry-client\"\n        self.authentication = authentication\n        self.retryOptions = retryOptions\n        self.bufferSize = bufferSize\n        var httpConfiguration = HTTPClient.Configuration()\n\n        // proxy configuration assumes all client requests will go to `base` URL\n        self.proxyURL = ProxyUtils.proxyFromEnvironment(scheme: scheme, host: host)\n        if let proxyURL = self.proxyURL, let proxyHost = proxyURL.host {\n            let proxyPort = proxyURL.port ?? (proxyURL.scheme == \"https\" ? 443 : 80)\n            httpConfiguration.proxy = HTTPClient.Configuration.Proxy.server(host: proxyHost, port: proxyPort)\n        }\n        if tlsConfiguration != nil {\n            httpConfiguration.tlsConfiguration = tlsConfiguration\n        }\n\n        if let logger {\n            self.client = HTTPClient(eventLoopGroupProvider: .singleton, configuration: httpConfiguration, backgroundActivityLogger: logger)\n        } else {\n            self.client = HTTPClient(eventLoopGroupProvider: .singleton, configuration: httpConfiguration)\n        }\n    }\n\n    deinit {\n        _ = client.shutdown()\n    }\n\n    func host() -> String {\n        base.host ?? \"\"\n    }\n\n    internal func request<T>(\n        components: URLComponents,\n        method: HTTPMethod = .GET,\n        bodyClosure: () throws -> HTTPClientRequest.Body? = { nil },\n        headers: [(String, String)]? = nil,\n        closure: (HTTPClientResponse) async throws -> T\n    ) async throws -> T {\n        guard let path = components.url?.absoluteString else {\n            throw ContainerizationError(.invalidArgument, message: \"invalid url \\(components.path)\")\n        }\n\n        var request = HTTPClientRequest(url: path)\n        request.method = method\n\n        var currentToken: TokenResponse?\n        let token: String? = try await {\n            if let basicAuth = authentication {\n                return try await basicAuth.token()\n            }\n            return nil\n        }()\n\n        if let token {\n            request.headers.add(name: \"Authorization\", value: \"\\(token)\")\n        }\n\n        // Add any arbitrary headers\n        headers?.forEach { (k, v) in request.headers.add(name: k, value: v) }\n        var retryCount = 0\n        var response: HTTPClientResponse?\n        while true {\n            request.body = try bodyClosure()\n            do {\n                let _response = try await client.execute(request, deadline: .distantFuture)\n                response = _response\n                if _response.status == .unauthorized || _response.status == .forbidden {\n                    let authHeader = _response.headers[TokenRequest.authenticateHeaderName]\n                    let tokenRequest: TokenRequest\n                    do {\n                        tokenRequest = try self.createTokenRequest(parsing: authHeader)\n                    } catch {\n                        // The server did not tell us how to authenticate our requests,\n                        // Or we do not support scheme the server is requesting for.\n                        // Throw the 401/403 to the caller, and let them decide how to proceed.\n                        throw RegistryClient.Error.invalidStatus(url: path, _response.status, reason: String(describing: error))\n                    }\n                    if let ct = currentToken, ct.isValid(scope: tokenRequest.scope) {\n                        break\n                    }\n\n                    do {\n                        let _currentToken = try await fetchToken(request: tokenRequest)\n                        guard let token = _currentToken.getToken() else {\n                            throw ContainerizationError(.internalError, message: \"failed to fetch Bearer token\")\n                        }\n                        currentToken = _currentToken\n                        request.headers.replaceOrAdd(name: \"Authorization\", value: token)\n                        retryCount += 1\n                    } catch let err as RegistryClient.Error {\n                        guard case .invalidStatus(_, let status, _) = err else {\n                            throw err\n                        }\n                        if status == .unauthorized || status == .forbidden {\n                            throw RegistryClient.Error.invalidStatus(url: path, _response.status, reason: \"access denied or wrong credentials\")\n                        }\n\n                        throw err\n                    }\n\n                    continue\n                }\n                guard let retryOptions = self.retryOptions else {\n                    break\n                }\n                guard retryCount < retryOptions.maxRetries else {\n                    break\n                }\n                guard let shouldRetry = retryOptions.shouldRetry, shouldRetry(_response) else {\n                    break\n                }\n                retryCount += 1\n                try await Task.sleep(nanoseconds: retryOptions.retryInterval)\n                continue\n            } catch let err as RegistryClient.Error {\n                throw err\n            } catch {\n                #if os(macOS)\n                if let err = error as? NWError {\n                    if err.errorCode == kDNSServiceErr_NoSuchRecord {\n                        let message: String\n                        if let proxyURL = self.proxyURL, let proxyHost = proxyURL.host {\n                            message = \"failed to resolve either repository hostname \\(host()) or proxy hostname \\(proxyHost)\"\n                        } else {\n                            message = \"failed to resolve either repository hostname \\(host())\"\n                        }\n                        throw ContainerizationError(.internalError, message: message)\n                    }\n                }\n                #endif\n                guard let retryOptions = self.retryOptions, retryCount < retryOptions.maxRetries else {\n                    throw error\n                }\n                retryCount += 1\n                try await Task.sleep(nanoseconds: retryOptions.retryInterval)\n            }\n        }\n        guard let response else {\n            throw ContainerizationError(.internalError, message: \"invalid response\")\n        }\n        return try await closure(response)\n    }\n\n    internal func requestData(\n        components: URLComponents,\n        headers: [(String, String)]? = nil\n    ) async throws -> Data {\n        let bytes: ByteBuffer = try await requestBuffer(components: components, headers: headers)\n        return Data(buffer: bytes)\n    }\n\n    internal func requestBuffer(\n        components: URLComponents,\n        headers: [(String, String)]? = nil\n    ) async throws -> ByteBuffer {\n        try await request(components: components, method: .GET, headers: headers) { response in\n            guard response.status == .ok else {\n                let url = components.url?.absoluteString ?? \"unknown\"\n                let reason = await ErrorResponse.fromResponseBody(response.body)?.jsonString\n                throw Error.invalidStatus(url: url, response.status, reason: reason)\n            }\n\n            return try await response.body.collect(upTo: self.bufferSize)\n        }\n    }\n\n    internal func requestJSON<T: Decodable>(\n        components: URLComponents,\n        headers: [(String, String)]? = nil\n    ) async throws -> T {\n        let buffer = try await self.requestBuffer(components: components, headers: headers)\n        return try JSONDecoder().decode(T.self, from: buffer)\n    }\n\n    /// A minimal endpoint, mounted at /v2/ will provide version support information based on its response statuses.\n    /// See https://distribution.github.io/distribution/spec/api/#api-version-check\n    public func ping() async throws {\n        var components = base\n        components.path = \"/v2/\"\n\n        try await request(components: components) { response in\n            guard response.status == .ok else {\n                let url = components.url?.absoluteString ?? \"unknown\"\n                let reason = await ErrorResponse.fromResponseBody(response.body)?.jsonString\n                throw Error.invalidStatus(url: url, response.status, reason: reason)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Content/AsyncTypes.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\npackage actor AsyncStore<T> {\n    private var _value: T?\n\n    package init(_ value: T? = nil) {\n        self._value = value\n    }\n\n    package func get() -> T? {\n        self._value\n    }\n\n    package func set(_ value: T) {\n        self._value = value\n    }\n}\n\npackage actor AsyncSet<T: Hashable> {\n    private var buffer: Set<T>\n\n    package init<S: Sequence>(_ elements: S) where S.Element == T {\n        buffer = Set(elements)\n    }\n\n    package var count: Int {\n        buffer.count\n    }\n\n    package func insert(_ element: T) {\n        buffer.insert(element)\n    }\n\n    @discardableResult\n    package func remove(_ element: T) -> T? {\n        buffer.remove(element)\n    }\n\n    package func contains(_ element: T) -> Bool {\n        buffer.contains(element)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Content/Content.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationExtras\nimport Crypto\nimport Foundation\nimport NIOCore\n\n/// Protocol for defining a single OCI content\npublic protocol Content: Sendable {\n    /// URL to the content\n    var path: URL { get }\n\n    /// sha256 of content\n    func digest() throws -> SHA256.Digest\n\n    /// Size of content\n    func size() throws -> UInt64\n\n    /// Data representation of entire content\n    func data() throws -> Data\n\n    /// Data representation partial content\n    func data(offset: UInt64, length: Int) throws -> Data?\n\n    /// Decode the content into an object\n    func decode<T>() throws -> T where T: Decodable\n}\n\n/// Protocol defining methods to fetch and push OCI content\npublic protocol ContentClient: Sendable {\n    func fetch<T: Codable>(name: String, descriptor: Descriptor) async throws -> T\n\n    func fetchBlob(name: String, descriptor: Descriptor, into file: URL, progress: ProgressHandler?) async throws -> (Int64, SHA256Digest)\n\n    func fetchData(name: String, descriptor: Descriptor) async throws -> Data\n\n    func push<T: Sendable & AsyncSequence>(\n        name: String,\n        ref: String,\n        descriptor: Descriptor,\n        streamGenerator: () throws -> T,\n        progress: ProgressHandler?\n    ) async throws where T.Element == ByteBuffer\n\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Content/ContentStoreProtocol.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Crypto\nimport Foundation\n\n/// Protocol for defining a content store where OCI image metadata and layers will be managed\n/// and manipulated.\npublic protocol ContentStore: Sendable {\n    /// Retrieves a piece of Content based on the digest string.\n    /// Returns `nil` if the requested digest is not found.\n    func get(digest: String) async throws -> Content?\n\n    /// Retrieves a specific content metadata type based on the digest string.\n    /// Returns `nil` if the requested digest is not found.\n    func get<T: Decodable>(digest: String) async throws -> T?\n\n    /// Remove a list of digests in the content store.\n    @discardableResult\n    func delete(digests: [String]) async throws -> ([String], UInt64)\n\n    /// Removes all content from the store except for the digests in the provided list.\n    @discardableResult\n    func delete(keeping: [String]) async throws -> ([String], UInt64)\n\n    /// Creates a transactional write to the content store.\n    /// The function takes a closure given a temporary `URL` of the base directory which all contents should be written to.\n    /// This is transaction write where any failed operation in the closure (caught exception) will result in all contents written\n    /// in the closure to be deleted.\n    ///\n    /// If the closure succeeds, then all the content that have been written to the temporary `URL` will be moved into the actual\n    /// blobs path of the content store.\n    @discardableResult\n    func ingest(_ body: @Sendable @escaping (URL) async throws -> Void) async throws -> [String]\n\n    /// Creates a new ingest session and returns the session ID and temporary ingest directory corresponding to the session.\n    /// The contents from the ingest directory are processed and moved into the content store once the session is marked complete.\n    /// This can be done by invoking the `completeIngestSession` method with the returned session ID.\n    func newIngestSession() async throws -> (id: String, ingestDir: URL)\n\n    /// Completes a previously started ingest session corresponding to `id`.\n    /// The contents from the ingest directory from the session are moved into the content store atomically.\n    /// Any failure encountered will result in a transaction failure causing none of the contents to be ingested into the store.\n    @discardableResult\n    func completeIngestSession(_ id: String) async throws -> [String]\n\n    /// Cancels a previously started ingest session corresponding to `id`.\n    /// The contents from the ingest directory corresponding to the session are removed.\n    func cancelIngestSession(_ id: String) async throws\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Content/ContentWriter.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport Crypto\nimport Foundation\nimport NIOCore\n\n/// Provides a context to write data into a directory.\npublic class ContentWriter {\n    private let base: URL\n    private let encoder = JSONEncoder()\n\n    /// Create a new ContentWriter.\n    /// - Parameters:\n    ///   - base: The URL to write content to. If this is not a directory a\n    ///           ContainerizationError will be thrown with a code of .internalError.\n    public init(for base: URL) throws {\n        self.encoder.outputFormatting = [JSONEncoder.OutputFormatting.sortedKeys]\n\n        self.base = base\n        var isDirectory = ObjCBool(true)\n        let exists = FileManager.default.fileExists(atPath: base.path, isDirectory: &isDirectory)\n\n        guard exists && isDirectory.boolValue else {\n            throw ContainerizationError(.internalError, message: \"cannot create ContentWriter for path \\(base.absolutePath()), not a directory\")\n        }\n    }\n\n    /// Writes the data blob to the base URL provided in the constructor.\n    /// - Parameters:\n    ///   - data: The data blob to write to a file under the base path.\n    @discardableResult\n    public func write(_ data: Data) throws -> (size: Int64, digest: SHA256.Digest) {\n        let digest = SHA256.hash(data: data)\n        let destination = base.appendingPathComponent(digest.encoded)\n        try data.write(to: destination)\n        return (Int64(data.count), digest)\n    }\n\n    /// Reads the data present in the passed in URL and writes it to the base path.\n    /// - Parameters:\n    ///   - url: The URL to read the data from.\n    @discardableResult\n    public func create(from url: URL) throws -> (size: Int64, digest: SHA256.Digest) {\n        let data = try Data(contentsOf: url)\n        return try self.write(data)\n    }\n\n    /// Encodes the passed in type as a JSON blob and writes it to the base path.\n    /// - Parameters:\n    ///   - content: The type to convert to JSON.\n    @discardableResult\n    public func create<T: Encodable>(from content: T) throws -> (size: Int64, digest: SHA256.Digest) {\n        let data = try self.encoder.encode(content)\n        return try self.write(data)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Content/LocalContent.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport Crypto\nimport Foundation\n\npublic final class LocalContent: Content {\n    public let path: URL\n    private let file: FileHandle\n\n    public init(path: URL) throws {\n        guard FileManager.default.fileExists(atPath: path.path) else {\n            throw ContainerizationError(.notFound, message: \"content at path \\(path.absolutePath())\")\n        }\n\n        self.file = try FileHandle(forReadingFrom: path)\n        self.path = path\n    }\n\n    public func digest() throws -> SHA256.Digest {\n        let bufferSize = 64 * 1024  // 64 KB\n        var hasher = SHA256()\n\n        try self.file.seek(toOffset: 0)\n        while case let data = file.readData(ofLength: bufferSize), !data.isEmpty {\n            hasher.update(data: data)\n        }\n\n        let digest = hasher.finalize()\n\n        try self.file.seek(toOffset: 0)\n        return digest\n    }\n\n    public func data(offset: UInt64 = 0, length size: Int = 0) throws -> Data? {\n        try file.seek(toOffset: offset)\n        if size == 0 {\n            return try file.readToEnd()\n        }\n        return try file.read(upToCount: size)\n    }\n\n    public func data() throws -> Data {\n        try Data(contentsOf: self.path)\n    }\n\n    public func size() throws -> UInt64 {\n        let fileAttrs = try FileManager.default.attributesOfItem(atPath: self.path.absolutePath())\n        if let size = fileAttrs[FileAttributeKey.size] as? UInt64 {\n            return size\n        }\n        throw ContainerizationError(.internalError, message: \"could not determine file size for \\(path.absolutePath())\")\n    }\n\n    public func decode<T>() throws -> T where T: Decodable {\n        let json = JSONDecoder()\n        let data = try Data(contentsOf: self.path)\n        return try json.decode(T.self, from: data)\n    }\n\n    deinit {\n        try? self.file.close()\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Content/LocalContentStore.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n// swiftlint:disable unused_optional_binding\n\nimport ContainerizationError\nimport ContainerizationExtras\nimport Crypto\nimport Foundation\n\n/// A `ContentStore` implementation that stores content on the local filesystem.\npublic actor LocalContentStore: ContentStore {\n    private static let encoder = JSONEncoder()\n\n    private let _basePath: URL\n    private let _ingestPath: URL\n    private let _blobPath: URL\n    private let _lock: AsyncLock\n\n    private var activeIngestSessions: AsyncSet<String> = AsyncSet([])\n\n    /// Create a new `LocalContentStore`.\n    ///\n    /// - Parameters:\n    ///   - path: The path where content should be written under.\n    public init(path: URL) throws {\n        let ingestPath = path.appendingPathComponent(\"ingest\")\n        let blobPath = path.appendingPathComponent(\"blobs/sha256\")\n\n        let fileManager = FileManager.default\n        try fileManager.createDirectory(at: ingestPath, withIntermediateDirectories: true)\n        try fileManager.createDirectory(at: blobPath, withIntermediateDirectories: true)\n\n        self._basePath = path\n        self._ingestPath = ingestPath\n        self._blobPath = blobPath\n        self._lock = AsyncLock()\n        Self.encoder.outputFormatting = .sortedKeys\n    }\n\n    /// Get a piece of content from the store. Returns nil if not\n    /// found.\n    ///\n    /// - Parameters:\n    ///   - digest: The string digest of the content.\n    public func get(digest: String) throws -> Content? {\n        let d = digest.trimmingDigestPrefix\n        let path = self._blobPath.appendingPathComponent(d)\n        do {\n            return try LocalContent(path: path)\n        } catch let err as ContainerizationError {\n            switch err.code {\n            case .notFound:\n                return nil\n            default:\n                throw err\n            }\n        }\n    }\n\n    /// Get a piece of content from the store and return the decoded version of\n    /// it.\n    ///\n    /// - Parameters:\n    ///   - digest: The string digest of the content.\n    public func get<T: Decodable & Sendable>(digest: String) throws -> T? {\n        guard let content: Content = try self.get(digest: digest) else {\n            return nil\n        }\n        return try content.decode()\n    }\n\n    /// Delete all content besides a set provided.\n    ///\n    /// - Parameters:\n    ///   - keeping: The set of string digests to keep.\n    public func delete(keeping: [String]) async throws -> ([String], UInt64) {\n        let fileManager = FileManager.default\n        let all = try fileManager.contentsOfDirectory(at: self._blobPath, includingPropertiesForKeys: nil)\n        let allDigests = Set(all.map { $0.lastPathComponent })\n        let toDelete = allDigests.subtracting(keeping)\n        return try await self.delete(digests: Array(toDelete))\n    }\n\n    /// Delete a specific set of content.\n    ///\n    /// - Parameters:\n    ///   - digests: Array of strings denoting the digests of the content to delete.\n    @discardableResult\n    public func delete(digests: [String]) async throws -> ([String], UInt64) {\n        let store = AsyncStore<([String], UInt64)>()\n        try await self._lock.withLock { context in\n            let fileManager = FileManager.default\n            var deleted: [String] = []\n            var deletedBytes: UInt64 = 0\n            for toDelete in digests {\n                let p = self._blobPath.appendingPathComponent(toDelete)\n                guard let content = try? LocalContent(path: p) else {\n                    continue\n                }\n                deletedBytes += try content.size()\n                try fileManager.removeItem(at: p)\n                deleted.append(toDelete)\n            }\n            await store.set((deleted, deletedBytes))\n        }\n        return await store.get() ?? ([], 0)\n    }\n\n    /// Creates a transactional write to the content store.\n    ///\n    /// - Parameters:\n    ///   - body: Closure that is given a temporary `URL` of the base directory which all contents should be written to.\n    /// This is a transaction write where any failed operation in the closure (caught exception) will result in all contents written\n    /// in the closure to be deleted. If the closure succeeds, then all the content that have been written to the temporary `URL`\n    /// will be moved into the actual blobs path of the content store.\n    @discardableResult\n    public func ingest(_ body: @Sendable @escaping (URL) async throws -> Void) async throws -> [String] {\n        let (id, tempPath) = try await self.newIngestSession()\n        try await body(tempPath)\n        return try await self.completeIngestSession(id)\n    }\n\n    /// Creates a new ingest session and returns the session ID and temporary ingest directory corresponding to the session.\n    /// The contents from the ingest directory are processed and moved into the content store once the session is marked complete.\n    /// This can be done by invoking the `completeIngestSession` method with the returned session ID.\n    public func newIngestSession() async throws -> (id: String, ingestDir: URL) {\n        let id = UUID().uuidString\n        let temporaryPath = self._ingestPath.appendingPathComponent(id)\n        let fileManager = FileManager.default\n        try fileManager.createDirectory(atPath: temporaryPath.path, withIntermediateDirectories: true)\n        await self.activeIngestSessions.insert(id)\n        return (id, temporaryPath)\n    }\n\n    /// Completes a previously started ingest session corresponding to `id`. The contents from the ingest\n    /// directory from the session are moved into the content store atomically. Any failure encountered will\n    /// result in a transaction failure causing none of the contents to be ingested into the store.\n    /// - Parameters:\n    ///   - id: id of the ingest session to complete.\n    @discardableResult\n    public func completeIngestSession(_ id: String) async throws -> [String] {\n        guard await activeIngestSessions.contains(id) else {\n            throw ContainerizationError(.internalError, message: \"invalid session id \\(id)\")\n        }\n        await activeIngestSessions.remove(id)\n        let temporaryPath = self._ingestPath.appendingPathComponent(id)\n        let fileManager = FileManager.default\n        defer {\n            try? fileManager.removeItem(at: temporaryPath)\n        }\n        let tempDigests: [URL] = try fileManager.contentsOfDirectory(at: temporaryPath, includingPropertiesForKeys: nil)\n        return try await self._lock.withLock { context in\n            var moved: [String] = []\n            let fileManager = FileManager.default\n            do {\n                try tempDigests.forEach {\n                    let digest = $0.lastPathComponent\n                    let target = self._blobPath.appendingPathComponent(digest)\n                    // only ingest if not exists\n                    if !fileManager.fileExists(atPath: target.path) {\n                        try fileManager.moveItem(at: $0, to: target)\n                        moved.append(digest)\n                    }\n                }\n            } catch {\n                moved.forEach {\n                    try? fileManager.removeItem(at: self._blobPath.appendingPathComponent($0))\n                }\n                throw error\n            }\n            return tempDigests.map { $0.lastPathComponent }\n        }\n    }\n\n    /// Cancels a previously started ingest session corresponding to `id`.\n    /// The contents from the ingest directory corresponding to the session are removed.\n    /// - Parameters:\n    ///   - id: id of the ingest session to complete.\n    public func cancelIngestSession(_ id: String) async throws {\n        guard let _ = await self.activeIngestSessions.remove(id) else {\n            return\n        }\n        let temporaryPath = self._ingestPath.appendingPathComponent(id)\n        let fileManager = FileManager.default\n        try? fileManager.removeItem(at: temporaryPath)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Content/SHA256+Extensions.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Crypto\nimport Foundation\n\nextension SHA256.Digest {\n    /// Returns the digest as a string.\n    public var digestString: String {\n        let parts = self.description.split(separator: \": \")\n        return \"sha256:\\(parts[1])\"\n    }\n\n    /// Returns the digest without a 'sha256:' prefix.\n    public var encoded: String {\n        let parts = self.description.split(separator: \": \")\n        return String(parts[1])\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Content/String+Extension.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nextension String {\n    /// Removes any prefix (sha256:) from a digest string.\n    public var trimmingDigestPrefix: String {\n        let split = self.split(separator: \":\")\n        if split.count == 2 {\n            return String(split[1])\n        }\n        return self\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Content/URL+Extensions.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\nextension URL {\n    /// Returns the unescaped absolutePath of a URL joined by separator.\n    public func absolutePath() -> String {\n        #if os(macOS)\n        return self.path(percentEncoded: false)\n        #else\n        return self.path\n        #endif\n    }\n\n    /// Returns the domain name of a registry.\n    public var domain: String? {\n        guard let host = self.absoluteString.split(separator: \":\").first else {\n            return nil\n        }\n        return String(host)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Descriptor.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//  Source: https://github.com/opencontainers/image-spec/blob/main/specs-go/v1/descriptor.go\n\nimport Foundation\n\n/// Descriptor describes the disposition of targeted content.\n/// This structure provides `application/vnd.oci.descriptor.v1+json` mediatype\n/// when marshalled to JSON.\npublic struct Descriptor: Codable, Sendable, Equatable {\n    /// mediaType is the media type of the object this schema refers to.\n    public let mediaType: String\n\n    /// digest is the digest of the targeted content.\n    public let digest: String\n\n    /// size specifies the size in bytes of the blob.\n    public let size: Int64\n\n    /// urls specifies a list of URLs from which this object MAY be downloaded.\n    public let urls: [String]?\n\n    /// annotations contains arbitrary metadata relating to the targeted content.\n    public var annotations: [String: String]?\n\n    /// platform describes the platform which the image in the manifest runs on.\n    ///\n    /// This should only be used when referring to a manifest.\n    public var platform: Platform?\n\n    /// artifactType specifies the IANA media type of the artifact.\n    ///\n    /// Used in referrers API responses to indicate the type of each referring artifact.\n    public let artifactType: String?\n\n    public init(\n        mediaType: String, digest: String, size: Int64, urls: [String]? = nil, annotations: [String: String]? = nil,\n        platform: Platform? = nil, artifactType: String? = nil\n    ) {\n        self.mediaType = mediaType\n        self.digest = digest\n        self.size = size\n        self.urls = urls\n        self.annotations = annotations\n        self.platform = platform\n        self.artifactType = artifactType\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/FileManager+Size.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\nextension FileManager {\n    func fileSize(atPath path: String) -> Int64? {\n        do {\n            let attributes = try attributesOfItem(atPath: path)\n            guard let fileSize = attributes[.size] as? NSNumber else {\n                return nil\n            }\n            return fileSize.int64Value\n        } catch {\n            return nil\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/ImageConfig.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//  Source: https://github.com/opencontainers/image-spec/blob/main/specs-go/v1/config.go\n\nimport Foundation\n\n/// ImageConfig defines the execution parameters which should be used as a base when running a container using an image.\npublic struct ImageConfig: Codable, Sendable {\n    enum CodingKeys: String, CodingKey {\n        case user = \"User\"\n        case env = \"Env\"\n        case entrypoint = \"Entrypoint\"\n        case cmd = \"Cmd\"\n        case workingDir = \"WorkingDir\"\n        case labels = \"Labels\"\n        case stopSignal = \"StopSignal\"\n    }\n\n    /// user defines the username or UID which the process in the container should run as.\n    public let user: String?\n\n    /// env is a list of environment variables to be used in a container.\n    public let env: [String]?\n\n    /// entrypoint defines a list of arguments to use as the command to execute when the container starts.\n    public let entrypoint: [String]?\n\n    /// cmd defines the default arguments to the entrypoint of the container.\n    public let cmd: [String]?\n\n    /// workingDir sets the current working directory of the entrypoint process in the container.\n    public let workingDir: String?\n\n    /// labels contains arbitrary metadata for the container.\n    public let labels: [String: String]?\n\n    /// stopSignal contains the system call signal that will be sent to the container to exit.\n    public let stopSignal: String?\n\n    public init(\n        user: String? = nil, env: [String]? = nil, entrypoint: [String]? = nil, cmd: [String]? = nil,\n        workingDir: String? = nil, labels: [String: String]? = nil, stopSignal: String? = nil\n    ) {\n        self.user = user\n        self.env = env\n        self.entrypoint = entrypoint\n        self.cmd = cmd\n        self.workingDir = workingDir\n        self.labels = labels\n        self.stopSignal = stopSignal\n    }\n}\n\n/// RootFS describes a layer content addresses\npublic struct Rootfs: Codable, Sendable {\n    enum CodingKeys: String, CodingKey {\n        case type\n        case diffIDs = \"diff_ids\"\n    }\n\n    /// type is the type of the rootfs.\n    public let type: String\n\n    /// diffIDs is an array of layer content hashes (DiffIDs), in order from bottom-most to top-most.\n    public let diffIDs: [String]\n\n    public init(type: String, diffIDs: [String]) {\n        self.type = type\n        self.diffIDs = diffIDs\n    }\n}\n\n/// History describes the history of a layer.\npublic struct History: Codable, Sendable {\n    enum CodingKeys: String, CodingKey {\n        case created\n        case createdBy = \"created_by\"\n        case author\n        case comment\n        case emptyLayer = \"empty_layer\"\n    }\n\n    /// created is the combined date and time at which the layer was created, formatted as defined by RFC 3339, section 5.6.\n    public let created: String?\n\n    /// createdBy is the command which created the layer.\n    public let createdBy: String?\n\n    /// author is the author of the build point.\n    public let author: String?\n\n    /// comment is a custom message set when creating the layer.\n    public let comment: String?\n\n    /// emptyLayer is used to mark if the history item created a filesystem diff.\n    public let emptyLayer: Bool?\n\n    public init(\n        created: String? = nil, createdBy: String? = nil, author: String? = nil, comment: String? = nil,\n        emptyLayer: Bool? = nil\n    ) {\n        self.created = created\n        self.createdBy = createdBy\n        self.author = author\n        self.comment = comment\n        self.emptyLayer = emptyLayer\n    }\n}\n\n/// Image is the JSON structure which describes some basic information about the image.\n/// This provides the `application/vnd.oci.image.config.v1+json` mediatype when marshalled to JSON.\npublic struct Image: Codable, Sendable {\n    /// created is the combined date and time at which the image was created, formatted as defined by RFC 3339, section 5.6.\n    public let created: String?\n\n    /// author defines the name and/or email address of the person or entity which created and is responsible for maintaining the image.\n    public let author: String?\n\n    /// architecture field specifies the CPU architecture, for example `amd64` or `ppc64`.\n    public let architecture: String\n\n    /// os specifies the operating system, for example `linux` or `windows`.\n    public let os: String\n\n    /// osVersion is an optional field specifying the operating system version, for example on Windows `10.0.14393.1066`.\n    public let osVersion: String?\n\n    /// osFeatures is an optional field specifying an array of strings, each listing a required OS feature (for example on Windows `win32k`).\n    public let osFeatures: [String]?\n\n    /// variant is an optional field specifying a variant of the CPU, for example `v7` to specify ARMv7 when architecture is `arm`.\n    public let variant: String?\n\n    /// config defines the execution parameters which should be used as a base when running a container using the image.\n    public let config: ImageConfig?\n\n    /// rootfs references the layer content addresses used by the image.\n    public let rootfs: Rootfs\n\n    /// history describes the history of each layer.\n    public let history: [History]?\n\n    public init(\n        created: String? = nil, author: String? = nil, architecture: String, os: String, osVersion: String? = nil,\n        osFeatures: [String]? = nil, variant: String? = nil, config: ImageConfig? = nil, rootfs: Rootfs,\n        history: [History]? = nil\n    ) {\n        self.created = created\n        self.author = author\n        self.architecture = architecture\n        self.os = os\n        self.osVersion = osVersion\n        self.osFeatures = osFeatures\n        self.variant = variant\n        self.config = config\n        self.rootfs = rootfs\n        self.history = history\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Index.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//  Source: https://github.com/opencontainers/image-spec/blob/main/specs-go/v1/index.go\n\nimport Foundation\n\n/// Index references manifests for various platforms.\n/// This structure provides `application/vnd.oci.image.index.v1+json` mediatype when marshalled to JSON.\npublic struct Index: Codable, Sendable {\n    /// schemaVersion is the image manifest schema that this image follows\n    public let schemaVersion: Int\n\n    /// mediaType specifies the type of this document data structure e.g. `application/vnd.oci.image.index.v1+json`\n    /// This field is optional per the OCI Image Index Specification (omitempty)\n    public let mediaType: String\n\n    /// manifests references platform specific manifests.\n    public var manifests: [Descriptor]\n\n    /// annotations contains arbitrary metadata for the image index.\n    public var annotations: [String: String]?\n\n    /// `subject` references another manifest this index is an artifact of.\n    public let subject: Descriptor?\n\n    /// `artifactType` specifies the IANA media type of the artifact this index represents.\n    public let artifactType: String?\n\n    public init(\n        schemaVersion: Int = 2, mediaType: String = MediaTypes.index, manifests: [Descriptor],\n        annotations: [String: String]? = nil, subject: Descriptor? = nil, artifactType: String? = nil\n    ) {\n        self.schemaVersion = schemaVersion\n        self.mediaType = mediaType\n        self.manifests = manifests\n        self.annotations = annotations\n        self.subject = subject\n        self.artifactType = artifactType\n    }\n\n    public init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        self.schemaVersion = try container.decode(Int.self, forKey: .schemaVersion)\n        self.mediaType = try container.decodeIfPresent(String.self, forKey: .mediaType) ?? \"\"\n        self.manifests = try container.decode([Descriptor].self, forKey: .manifests)\n        self.annotations = try container.decodeIfPresent([String: String].self, forKey: .annotations)\n        self.subject = try container.decodeIfPresent(Descriptor.self, forKey: .subject)\n        self.artifactType = try container.decodeIfPresent(String.self, forKey: .artifactType)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Manifest.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//  Source: https://github.com/opencontainers/image-spec/blob/main/specs-go/v1/manifest.go\n\nimport Foundation\n\n/// Manifest provides `application/vnd.oci.image.manifest.v1+json` mediatype structure when marshalled to JSON.\npublic struct Manifest: Codable, Sendable {\n    /// `schemaVersion` is the image manifest schema that this image follows.\n    public let schemaVersion: Int\n\n    /// `mediaType` specifies the type of this document data structure, e.g. `application/vnd.oci.image.manifest.v1+json`.\n    public let mediaType: String?\n\n    /// `config` references a configuration object for a container, by digest.\n    /// The referenced configuration object is a JSON blob that the runtime uses to set up the container.\n    public let config: Descriptor\n\n    /// `layers` is an indexed list of layers referenced by the manifest.\n    public let layers: [Descriptor]\n\n    /// `annotations` contains arbitrary metadata for the image manifest.\n    public let annotations: [String: String]?\n\n    /// `subject` references another manifest this manifest is an artifact of.\n    public let subject: Descriptor?\n\n    /// `artifactType` specifies the IANA media type of the artifact this manifest represents.\n    public let artifactType: String?\n\n    public init(\n        schemaVersion: Int = 2, mediaType: String = MediaTypes.imageManifest, config: Descriptor, layers: [Descriptor],\n        annotations: [String: String]? = nil, subject: Descriptor? = nil, artifactType: String? = nil\n    ) {\n        self.schemaVersion = schemaVersion\n        self.mediaType = mediaType\n        self.config = config\n        self.layers = layers\n        self.annotations = annotations\n        self.subject = subject\n        self.artifactType = artifactType\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/MediaType.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// MediaTypes represent all supported OCI image content types for both metadata and layer formats.\n/// Follows all distributable media types in: https://github.com/opencontainers/image-spec/blob/main/specs-go/v1/mediatype.go\npublic struct MediaTypes: Codable, Sendable {\n    /// Specifies the media type for a content descriptor.\n    public static let descriptor = \"application/vnd.oci.descriptor.v1+json\"\n\n    /// Specifies the media type for the oci-layout.\n    public static let layoutHeader = \"application/vnd.oci.layout.header.v1+json\"\n\n    /// Specifies the media type for an image index.\n    public static let index = \"application/vnd.oci.image.index.v1+json\"\n\n    /// Specifies the media type for an image manifest.\n    public static let imageManifest = \"application/vnd.oci.image.manifest.v1+json\"\n\n    /// Specifies the media type for the image configuration.\n    public static let imageConfig = \"application/vnd.oci.image.config.v1+json\"\n\n    /// Specifies the media type for an unused blob containing the value \"{}\".\n    public static let emptyJSON = \"application/vnd.oci.empty.v1+json\"\n\n    /// Specifies the media type for a Docker image manifest.\n    public static let dockerManifest = \"application/vnd.docker.distribution.manifest.v2+json\"\n\n    /// Specifies the media type for a Docker image manifest list.\n    public static let dockerManifestList = \"application/vnd.docker.distribution.manifest.list.v2+json\"\n\n    /// The Docker media type used for image configurations.\n    public static let dockerImageConfig = \"application/vnd.docker.container.image.v1+json\"\n\n    /// The media type used for layers referenced by the manifest.\n    public static let imageLayer = \"application/vnd.oci.image.layer.v1.tar\"\n\n    /// The media type used for gzipped layers referenced by the manifest.\n    public static let imageLayerGzip = \"application/vnd.oci.image.layer.v1.tar+gzip\"\n\n    /// The media type used for zstd compressed layers referenced by the manifest.\n    public static let imageLayerZstd = \"application/vnd.oci.image.layer.v1.tar+zstd\"\n\n    /// The Docker media type used for uncompressed layers referenced by an image manifest.\n    public static let dockerImageLayer = \"application/vnd.docker.image.rootfs.diff.tar\"\n\n    /// The Docker media type used for gzipped layers referenced by an image manifest.\n    public static let dockerImageLayerGzip = \"application/vnd.docker.image.rootfs.diff.tar.gzip\"\n\n    /// The Docker media type used for zstd compressed layers referenced by an image manifest.\n    public static let dockerImageLayerZstd = \"application/vnd.docker.image.rootfs.diff.tar.zstd\"\n\n    /// The media type used for in-toto attestations blobs.\n    public static let inTotoAttestationBlob = \"application/vnd.in-toto+json\"\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Platform.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//  Source: https://github.com/opencontainers/image-spec/blob/main/specs-go/v1/config.go\n\nimport ContainerizationError\nimport Foundation\n\n/// Platform describes the platform which the image in the manifest runs on.\npublic struct Platform: Sendable, Equatable {\n    public static var current: Self {\n        var systemInfo = utsname()\n        uname(&systemInfo)\n        let arch = withUnsafePointer(to: &systemInfo.machine) {\n            $0.withMemoryRebound(to: CChar.self, capacity: 1) {\n                String(cString: $0)\n            }\n        }\n        switch arch {\n        case \"arm64\":\n            return .init(arch: \"arm64\", os: \"linux\", variant: \"v8\")\n        case \"x86_64\":\n            return .init(arch: \"amd64\", os: \"linux\")\n        default:\n            fatalError(\"unsupported arch \\(arch)\")\n        }\n    }\n\n    /// The computed description, for example, `linux/arm64/v8`.\n    public var description: String {\n        let architecture = architecture\n        if let variant = variant {\n            return \"\\(os)/\\(architecture)/\\(variant)\"\n        }\n        return \"\\(os)/\\(architecture)\"\n    }\n\n    /// The CPU architecture, for example, `amd64` or `ppc64`.\n    public var architecture: String {\n        switch _rawArch {\n        case \"arm64\", \"aarch64\":\n            return \"arm64\"\n        case \"x86_64\", \"x86-64\", \"amd64\":\n            return \"amd64\"\n        case \"386\", \"ppc64le\", \"i386\", \"s390x\", \"riscv64\":\n            return _rawArch\n        default:\n            return _rawArch\n        }\n    }\n\n    /// The operating system, for example, `linux` or `windows`.\n    public var os: String {\n        _rawOS\n    }\n\n    /// An optional field specifying the operating system version, for example on Windows `10.0.14393.1066`.\n    public var osVersion: String?\n\n    /// An optional field specifying an array of strings, each listing a required OS feature (for example on Windows `win32k`).\n    public var osFeatures: [String]?\n\n    /// An optional field specifying a variant of the CPU, for example `v7` to specify ARMv7 when architecture is `arm`.\n    public var variant: String?\n\n    /// The operation system of the image (eg. `linux`).\n    private let _rawOS: String\n    /// The CPU architecture (eg. `arm64`).\n    private let _rawArch: String\n\n    public init(arch: String, os: String, osVersion: String? = nil, osFeatures: [String]? = nil, variant: String? = nil) {\n        self._rawArch = arch\n        self._rawOS = os\n        self.osVersion = osVersion\n        self.osFeatures = osFeatures\n        self.variant = variant\n    }\n\n    ///     Initializes a new platform from a string.\n    ///     - Parameters:\n    ///        -  platform: A `string` value representing the platform.\n    ///     ```swift\n    ///     // Create a new `ImagePlatform` from string.\n    ///     let platform = try Platform(from: \"linux/amd64\")\n    ///     ```\n    ///     ## Throws ##\n    ///     - Throws:  `Error.missingOS` if input is empty\n    ///     - Throws:  `Error.invalidOS` if os is not `linux`\n    ///     - Throws:  `Error.missingArch` if only one `/` is present\n    ///     - Throws:  `Error.invalidArch` if an unrecognized architecture is provided\n    ///     - Throws:  `Error.invalidVariant` if a variant is provided, and it does not apply to the specified architecture\n    public init(from platform: String) throws {\n        let items = platform.split(separator: \"/\", maxSplits: 1)\n        guard let osValue = items.first else {\n            throw ContainerizationError(.invalidArgument, message: \"missing OS in \\(platform)\")\n        }\n        switch osValue {\n        case \"linux\":\n            _rawOS = osValue.description\n        case \"darwin\":\n            _rawOS = osValue.description\n        case \"windows\":\n            _rawOS = osValue.description\n        default:\n            throw ContainerizationError(.invalidArgument, message: \"unknown OS in \\(osValue)\")\n        }\n        guard items.count > 1 else {\n            throw ContainerizationError(.invalidArgument, message: \"missing architecture in \\(platform)\")\n        }\n\n        guard let archItems = items.last?.split(separator: \"/\", maxSplits: 1, omittingEmptySubsequences: false) else {\n            throw ContainerizationError(.invalidArgument, message: \"missing architecture in \\(platform)\")\n        }\n\n        guard let archName = archItems.first else {\n            throw ContainerizationError(.invalidArgument, message: \"missing architecture in \\(platform)\")\n        }\n\n        switch archName {\n        case \"arm\", \"armhf\", \"armel\":\n            _rawArch = \"arm\"\n            variant = \"v7\"\n        case \"aarch64\", \"arm64\":\n            variant = \"v8\"\n            _rawArch = \"arm64\"\n        case \"x86_64\", \"x86-64\", \"amd64\":\n            _rawArch = \"amd64\"\n        default:\n            _rawArch = archName.description\n        }\n\n        if archItems.count == 2 {\n            guard let archVariant = archItems.last else {\n                throw ContainerizationError(.invalidArgument, message: \"missing variant in \\(platform)\")\n            }\n\n            switch archName {\n            case \"arm\":\n                switch archVariant {\n                case \"v5\", \"v6\", \"v7\", \"v8\":\n                    variant = archVariant.description\n                default:\n                    throw ContainerizationError(.invalidArgument, message: \"invalid variant \\(archVariant)\")\n                }\n            case \"armhf\":\n                switch archVariant {\n                case \"v7\":\n                    variant = \"v7\"\n                default:\n                    throw ContainerizationError(.invalidArgument, message: \"invalid variant \\(archVariant)\")\n                }\n            case \"armel\":\n                switch archVariant {\n                case \"v6\":\n                    variant = \"v6\"\n                default:\n                    throw ContainerizationError(.invalidArgument, message: \"invalid variant \\(archVariant)\")\n                }\n            case \"aarch64\", \"arm64\":\n                switch archVariant {\n                case \"v8\", \"8\":\n                    variant = \"v8\"\n                default:\n                    throw ContainerizationError(.invalidArgument, message: \"invalid variant \\(archVariant)\")\n                }\n            case \"x86_64\", \"x86-64\", \"amd64\":\n                switch archVariant {\n                case \"v1\":\n                    variant = nil\n                default:\n                    throw ContainerizationError(.invalidArgument, message: \"invalid variant \\(archVariant)\")\n                }\n            case \"i386\", \"386\", \"ppc64le\", \"riscv64\":\n                throw ContainerizationError(.invalidArgument, message: \"invalid variant \\(archVariant)\")\n            default:\n                throw ContainerizationError(.invalidArgument, message: \"invalid variant \\(archVariant)\")\n            }\n        }\n    }\n\n}\n\nextension Platform: Hashable {\n    ///  `~=` compares two platforms to check if **lhs** platform images are compatible with **rhs** platform\n    ///  This operator can be used to check if an image of **lhs** platform can run on **rhs**:\n    ///  - `true`:  when **rhs**=`arm/v8`, **lhs** is any of `arm/v8`, `arm/v7`, `arm/v6` and `arm/v5`\n    ///  - `true`:  when **rhs**=`arm/v7`, **lhs** is any of `arm/v7`, `arm/v6` and `arm/v5`\n    ///  - `true`:  when **rhs**=`arm/v6`, **lhs** is any of `arm/v6` and `arm/v5`\n    ///  - `true`:  when **rhs**=`amd64`, **lhs** is any of `amd64` and `386`\n    ///  - `true`:  when **rhs**=**lhs**\n    ///  - `false`:  otherwise\n    ///  - Parameters:\n    ///     - lhs: platform whose compatibility is being checked\n    ///     - rhs: platform against which compatibility is being checked\n    ///  - Returns: `true | false`\n    public static func ~= (lhs: Platform, rhs: Platform) -> Bool {\n        if lhs.os == rhs.os {\n            if lhs._rawArch == rhs._rawArch {\n                switch rhs._rawArch {\n                case \"arm\":\n                    guard let lVariant = lhs.variant else {\n                        return lhs == rhs\n                    }\n                    guard let rVariant = rhs.variant else {\n                        return lhs == rhs\n                    }\n                    switch rVariant {\n                    case \"v8\":\n                        switch lVariant {\n                        case \"v5\", \"v6\", \"v7\", \"v8\":\n                            return true\n                        default:\n                            return false\n                        }\n                    case \"v7\":\n                        switch lVariant {\n                        case \"v5\", \"v6\", \"v7\":\n                            return true\n                        default:\n                            return false\n                        }\n                    case \"v6\":\n                        switch lVariant {\n                        case \"v5\", \"v6\":\n                            return true\n                        default:\n                            return false\n                        }\n                    default:\n                        return lhs == rhs\n                    }\n                default:\n                    return lhs == rhs\n                }\n            }\n            if lhs._rawArch == \"386\" && rhs._rawArch == \"amd64\" {\n                return true\n            }\n        }\n        return false\n    }\n\n    /// `==` compares if **lhs** and **rhs** are the exact same platforms.\n    public static func == (lhs: Platform, rhs: Platform) -> Bool {\n        //  NOTE:\n        //  If the platform struct was created by setting the fields directly and not using (from: String)\n        //  then, there is a possibility that for arm64 architecture, the variant may be set to nil\n        //  In that case, the variant should be assumed to v8\n        if lhs.architecture == \"arm64\" && rhs.architecture == \"arm64\" {\n            // The following checks effectively verify\n            // that one operand has nil value and other has \"v8\"\n            if lhs.variant == nil || rhs.variant == nil {\n                if lhs.variant == \"v8\" || rhs.variant == \"v8\" {\n                    return true\n                }\n            }\n        }\n\n        let osEqual = lhs.os == rhs.os\n        let archEqual = lhs.architecture == rhs.architecture\n        let variantEqual = lhs.variant == rhs.variant\n\n        return osEqual && archEqual && variantEqual\n    }\n\n    public func hash(into hasher: inout Swift.Hasher) {\n        hasher.combine(description)\n    }\n}\n\nextension Platform: Codable {\n\n    enum CodingKeys: String, CodingKey {\n        case os = \"os\"\n        case architecture = \"architecture\"\n        case variant = \"variant\"\n    }\n\n    public func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(os, forKey: .os)\n        try container.encode(architecture, forKey: .architecture)\n        try container.encodeIfPresent(variant, forKey: .variant)\n    }\n\n    public init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        let architecture = try container.decodeIfPresent(String.self, forKey: .architecture)\n        guard let architecture else {\n            throw ContainerizationError(.invalidArgument, message: \"missing architecture\")\n        }\n        let os = try container.decodeIfPresent(String.self, forKey: .os)\n        guard let os else {\n            throw ContainerizationError(.invalidArgument, message: \"missing OS\")\n        }\n        let variant = try container.decodeIfPresent(String.self, forKey: .variant)\n        self.init(arch: architecture, os: os, variant: variant)\n    }\n}\n\npublic func createPlatformMatcher(for platform: Platform?) -> @Sendable (Platform) -> Bool {\n    if let platform {\n        return { other in\n            platform == other\n        }\n    }\n    return { _ in\n        true\n    }\n}\n\npublic func filterPlatforms(matcher: (Platform) -> Bool, _ descriptors: [Descriptor]) throws -> [Descriptor] {\n    var outDescriptors: [Descriptor] = []\n    for desc in descriptors {\n        guard let p = desc.platform else {\n            // pass along descriptor if the platform is not defined\n            outDescriptors.append(desc)\n            continue\n        }\n        if matcher(p) {\n            outDescriptors.append(desc)\n        }\n    }\n    return outDescriptors\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Reference.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport Foundation\n\n// nameTotalLengthMax matches the OCI distribution spec which allows up to 255 bytes for the\n// repository name component (domain + \"/\" + path).\nprivate let nameTotalLengthMax = 255\n// referenceTotalLengthMax is the upper bound for the full reference string: max name (255) +\n// separator (1) + max tag length (128) = 384.\nprivate let tagLengthMax = 128\nprivate let referenceTotalLengthMax = nameTotalLengthMax + 1 + tagLengthMax\nprivate let legacyDockerRegistryHost = \"docker.io\"\nprivate let dockerRegistryHost = \"registry-1.docker.io\"\nprivate let defaultDockerRegistryRepo = \"library\"\nprivate let defaultTag = \"latest\"\n\n/// A Reference is composed of the various parts of an OCI image reference.\n/// For example:\n///     let imageReference = \"my-registry.com/repository/image:tag2\"\n///     let reference = Reference.parse(imageReference)\n///     print(reference.domain!) // gives us \"my-registry.com\"\n///     print(reference.name) // gives us \"my-registry.com/repository/image\"\n///     print(reference.path) // gives us \"repository/image\"\n///     print(reference.tag!) // gives us \"tag2\"\n///     print(reference.digest) // gives us \"nil\"\npublic class Reference: CustomStringConvertible {\n    private var _domain: String?\n    public var domain: String? {\n        _domain\n    }\n    public var resolvedDomain: String? {\n        if let d = _domain {\n            return Self.resolveDomain(domain: d)\n        }\n        return nil\n    }\n\n    private var _path: String\n    public var path: String {\n        _path\n    }\n\n    private var _tag: String?\n    public var tag: String? {\n        _tag\n    }\n\n    private var _digest: String?\n    public var digest: String? {\n        _digest\n    }\n\n    public var name: String {\n        if let domain, !domain.isEmpty {\n            return \"\\(domain)/\\(path)\"\n        }\n        return path\n    }\n\n    public var description: String {\n        if let tag {\n            return \"\\(name):\\(tag)\"\n        }\n        if let digest {\n            return \"\\(name)@\\(digest)\"\n        }\n        return name\n    }\n\n    static let identifierPattern = \"([a-f0-9]{64})\"\n\n    static let domainPattern = {\n        let domainNameComponent = \"(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\"\n        let optionalPort = \"(?::[0-9]+)?\"\n        let ipv6address = \"\\\\[(?:[a-fA-F0-9:]+)\\\\]\"\n        let domainName = \"\\(domainNameComponent)(?:\\\\.\\(domainNameComponent))*\"\n        let host = \"(?:\\(domainName)|\\(ipv6address))\"\n        let domainAndPort = \"\\(host)\\(optionalPort)\"\n        return domainAndPort\n    }()\n\n    static let pathPattern = \"(?<path>(?:[a-z0-9]+(?:[._]|__|-|/)?)*[a-z0-9]+)\"\n    static let tagPattern = \"(?::(?<tag>[\\\\w][\\\\w.-]{0,127}))?(?:@(?<digest>sha256:[0-9a-fA-F]{64}))?\"\n    static let pathTagPattern = \"\\(pathPattern)\\(tagPattern)\"\n\n    public init(path: String, domain: String? = nil, tag: String? = nil, digest: String? = nil) throws {\n        if let domain, !domain.isEmpty {\n            self._domain = domain\n        }\n\n        self._path = path\n        self._tag = tag\n        self._digest = digest\n    }\n\n    public static func parse(_ s: String) throws -> Reference {\n        if s.count > referenceTotalLengthMax {\n            throw ContainerizationError(.invalidArgument, message: \"reference length \\(s.count) greater than \\(referenceTotalLengthMax)\")\n        }\n\n        let identifierRegex = try Regex(Self.identifierPattern)\n        guard try identifierRegex.wholeMatch(in: s) == nil else {\n            throw ContainerizationError(.invalidArgument, message: \"cannot specify 64 byte hex string as reference\")\n        }\n\n        let (domain, remainder) = try Self.parseDomain(from: s)\n        let constructedRawReference: String = remainder\n        if let domain {\n            let domainRegex = try Regex(domainPattern)\n            guard try domainRegex.wholeMatch(in: domain) != nil else {\n                throw ContainerizationError(.invalidArgument, message: \"invalid domain \\(domain) for reference \\(s)\")\n            }\n        }\n        let fields = try constructedRawReference.matches(regex: pathTagPattern)\n        guard let path = fields[\"path\"] else {\n            throw ContainerizationError(.invalidArgument, message: \"cannot parse path for reference \\(s)\")\n        }\n\n        let ref = try Reference(path: path, domain: domain)\n        if ref.name.count > nameTotalLengthMax {\n            throw ContainerizationError(.invalidArgument, message: \"repo length \\(ref.name.count) greater than \\(nameTotalLengthMax)\")\n        }\n\n        // Extract tag and digest\n        let tag = fields[\"tag\"] ?? \"\"\n        let digest = fields[\"digest\"] ?? \"\"\n\n        if !digest.isEmpty {\n            return try ref.withDigest(digest)\n        } else if !tag.isEmpty {\n            return try ref.withTag(tag)\n        }\n        return ref\n    }\n\n    private static func parseDomain(from s: String) throws -> (domain: String?, remainder: String) {\n        var domain: String? = nil\n        var path: String = s\n        let charset = CharacterSet(charactersIn: \".:\")\n        let splits = s.split(separator: \"/\", maxSplits: 1)\n        guard splits.count == 2 else {\n            if s.starts(with: \"localhost\") {\n                return (s, \"\")\n            }\n            return (nil, s)\n        }\n        let _domain = String(splits[0])\n        let _path = String(splits[1])\n        if _domain.starts(with: \"localhost\") || _domain.rangeOfCharacter(from: charset) != nil {\n            domain = _domain\n            path = _path\n        }\n        return (domain, path)\n    }\n\n    public static func withName(_ name: String) throws -> Reference {\n        if name.count > nameTotalLengthMax {\n            throw ContainerizationError(.invalidArgument, message: \"name length \\(name.count) greater than \\(nameTotalLengthMax)\")\n        }\n        let fields = try name.matches(regex: Self.domainPattern)\n        // Extract domain and path\n        let domain = fields[\"domain\"] ?? \"\"\n        let path = fields[\"path\"] ?? \"\"\n\n        if domain.isEmpty || path.isEmpty {\n            throw ContainerizationError(.invalidArgument, message: \"image reference domain or path is empty\")\n        }\n\n        return try Reference(path: path, domain: domain)\n    }\n\n    public func withTag(_ tag: String) throws -> Reference {\n        var tag = tag\n        if !tag.starts(with: \":\") {\n            tag = \":\" + tag\n        }\n        let fields = try tag.matches(regex: Self.tagPattern)\n        tag = fields[\"tag\"] ?? \"\"\n\n        if tag.isEmpty {\n            throw ContainerizationError(.invalidArgument, message: \"invalid format for image reference, missing tag\")\n        }\n        return try Reference(path: self.path, domain: self.domain, tag: tag)\n    }\n\n    public func withDigest(_ digest: String) throws -> Reference {\n        var digest = digest\n        if !digest.starts(with: \"@\") {\n            digest = \"@\" + digest\n        }\n        let fields = try digest.matches(regex: Self.tagPattern)\n        digest = fields[\"digest\"] ?? \"\"\n\n        if digest.isEmpty {\n            throw ContainerizationError(.invalidArgument, message: \"invalid format for image reference, missing digest\")\n        }\n        return try Reference(path: self.path, domain: self.domain, digest: digest)\n    }\n\n    private static func splitDomain(_ name: String) -> (domain: String, path: String) {\n        let parts = name.split(separator: \"/\")\n        guard parts.count == 2 else {\n            return (\"\", name)\n        }\n        return (String(parts[0]), String(parts[1]))\n    }\n\n    /// Normalize the reference object.\n    /// Normalization is useful in cases where the reference object is to be used to\n    /// fetch/push an image from/to a remote registry.\n    /// It does the following:\n    /// - Adds a default tag of \"latest\" if the reference had no tag/digest set.\n    /// - If the domain is \"registry-1.docker.io\" or \"docker.io\" and the path has no repository set,\n    ///   it adds a default \"library/\" repository name.\n    public func normalize() {\n        if let domain = self.domain, domain == dockerRegistryHost || domain == legacyDockerRegistryHost {\n            // Check if the image is being referenced by a named tag.\n            // If it is, and a repository is not specified, prefix it with \"library/\".\n            // This needs to be done only if we are using the Docker registry.\n            if !self.path.contains(\"/\") {\n                self._path = \"\\(defaultDockerRegistryRepo)/\\(self._path)\"\n            }\n        }\n        let identifier = self._tag ?? self._digest\n        if identifier == nil {\n            // If the user did not specify a tag or a digest for the reference, set the tag to \"latest\".\n            self._tag = defaultTag\n        }\n    }\n\n    public static func resolveDomain(domain: String) -> String {\n        if domain == legacyDockerRegistryHost {\n            return dockerRegistryHost\n        }\n        return domain\n    }\n}\n\nextension String {\n    func matches(regex: String) throws -> [String: String] {\n        do {\n            let regex = try NSRegularExpression(pattern: regex, options: [])\n            let nsRange = NSRange(self.startIndex..<self.endIndex, in: self)\n            guard let match = regex.firstMatch(in: self, options: [], range: nsRange), match.range == nsRange else {\n                throw ContainerizationError(.invalidArgument, message: \"invalid format for image reference\")\n            }\n            var results = [String: String]()\n            for name in try regex.captureGroupNames() {\n                if let range = Range(match.range(withName: name), in: self) {\n                    results[name] = String(self[range])\n                }\n            }\n            return results\n        } catch {\n            throw error\n        }\n    }\n}\n\nextension NSRegularExpression {\n    func captureGroupNames() throws -> [String] {\n        let pattern = self.pattern\n        let regex = try NSRegularExpression(pattern: \"\\\\(\\\\?<(\\\\w+)>\", options: [])\n        let nsRange = NSRange(pattern.startIndex..<pattern.endIndex, in: pattern)\n        let matches = regex.matches(in: pattern, options: [], range: nsRange)\n        return matches.map {\n            String(pattern[Range($0.range(at: 1), in: pattern)!])\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Spec.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// NOTE: This is not a complete recreation of the runtime spec. Other platforms outside of Linux\n/// have been left off, and some APIs for Linux aren't present. This was manually ported starting\n/// at the v1.2.0 release.\n\npublic struct Spec: Codable, Sendable {\n    public var version: String\n    public var hooks: Hook?\n    public var process: Process?\n    public var hostname, domainname: String\n    public var mounts: [Mount]\n    public var annotations: [String: String]?\n    public var root: Root?\n    public var linux: Linux?\n\n    public init(\n        version: String = \"\",\n        hooks: Hook? = nil,\n        process: Process? = nil,\n        hostname: String = \"\",\n        domainname: String = \"\",\n        mounts: [Mount] = [],\n        annotations: [String: String]? = nil,\n        root: Root? = nil,\n        linux: Linux? = nil\n    ) {\n        self.version = version\n        self.hooks = hooks\n        self.process = process\n        self.hostname = hostname\n        self.domainname = domainname\n        self.mounts = mounts\n        self.annotations = annotations\n        self.root = root\n        self.linux = linux\n    }\n\n    public enum CodingKeys: String, CodingKey {\n        case version = \"ociVersion\"\n        case hooks\n        case process\n        case hostname\n        case domainname\n        case mounts\n        case annotations\n        case root\n        case linux\n    }\n\n    public init(from decoder: Decoder) throws {\n        self.init()\n\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        self.version = try container.decode(String.self, forKey: .version)\n        self.hooks = try container.decodeIfPresent(Hook.self, forKey: .hooks)\n        self.process = try container.decodeIfPresent(Process.self, forKey: .process)\n        if let hostname = try container.decodeIfPresent(String.self, forKey: .hostname) {\n            self.hostname = hostname\n        }\n        if let domainname = try container.decodeIfPresent(String.self, forKey: .domainname) {\n            self.domainname = domainname\n        }\n        if let mounts = try container.decodeIfPresent([Mount].self, forKey: .mounts) {\n            self.mounts = mounts\n        }\n        self.annotations = try container.decodeIfPresent([String: String].self, forKey: .annotations)\n        self.root = try container.decodeIfPresent(Root.self, forKey: .root)\n        self.linux = try container.decodeIfPresent(Linux.self, forKey: .linux)\n    }\n}\n\npublic struct Process: Codable, Sendable {\n    public var cwd: String\n    public var env: [String]\n    public var consoleSize: Box?\n    public var selinuxLabel: String\n    public var noNewPrivileges: Bool\n    public var commandLine: String\n    public var oomScoreAdj: Int?\n    public var capabilities: LinuxCapabilities?\n    public var apparmorProfile: String\n    public var user: User\n    public var rlimits: [POSIXRlimit]\n    public var args: [String]\n    public var terminal: Bool\n\n    public enum CodingKeys: String, CodingKey {\n        case cwd\n        case env\n        case consoleSize\n        case selinuxLabel\n        case noNewPrivileges\n        case commandLine\n        case oomScoreAdj\n        case capabilities\n        case apparmorProfile\n        case user\n        case rlimits\n        case args\n        case terminal\n    }\n\n    public init(\n        args: [String] = [],\n        cwd: String = \"/\",\n        env: [String] = [],\n        consoleSize: Box? = nil,\n        selinuxLabel: String = \"\",\n        noNewPrivileges: Bool = false,\n        commandLine: String = \"\",\n        oomScoreAdj: Int? = nil,\n        capabilities: LinuxCapabilities? = nil,\n        apparmorProfile: String = \"\",\n        user: User = .init(),\n        rlimits: [POSIXRlimit] = [],\n        terminal: Bool = false\n    ) {\n        self.cwd = cwd\n        self.env = env\n        self.consoleSize = consoleSize\n        self.selinuxLabel = selinuxLabel\n        self.noNewPrivileges = noNewPrivileges\n        self.commandLine = commandLine\n        self.oomScoreAdj = oomScoreAdj\n        self.capabilities = capabilities\n        self.apparmorProfile = apparmorProfile\n        self.user = user\n        self.rlimits = rlimits\n        self.args = args\n        self.terminal = terminal\n    }\n\n    public init(from config: ImageConfig) {\n        let cwd = config.workingDir ?? \"/\"\n        let env = config.env ?? []\n        let args = (config.entrypoint ?? []) + (config.cmd ?? [])\n        let user: User = {\n            if let rawString = config.user {\n                return User(username: rawString)\n            }\n            return User()\n        }()\n        self.init(args: args, cwd: cwd, env: env, user: user)\n    }\n\n    public init(from decoder: Decoder) throws {\n        self.init()\n\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        self.cwd = try container.decode(String.self, forKey: .cwd)\n        if let env = try container.decodeIfPresent([String].self, forKey: .env) {\n            self.env = env\n        }\n        self.consoleSize = try container.decodeIfPresent(Box.self, forKey: .consoleSize)\n        if let selinuxLabel = try container.decodeIfPresent(String.self, forKey: .selinuxLabel) {\n            self.selinuxLabel = selinuxLabel\n        }\n        if let noNewPrivileges = try container.decodeIfPresent(Bool.self, forKey: .noNewPrivileges) {\n            self.noNewPrivileges = noNewPrivileges\n        }\n        if let commandLine = try container.decodeIfPresent(String.self, forKey: .commandLine) {\n            self.commandLine = commandLine\n        }\n        self.oomScoreAdj = try container.decodeIfPresent(Int.self, forKey: .oomScoreAdj)\n        self.capabilities = try container.decodeIfPresent(LinuxCapabilities.self, forKey: .capabilities)\n        if let apparmorProfile = try container.decodeIfPresent(String.self, forKey: .apparmorProfile) {\n            self.apparmorProfile = apparmorProfile\n        }\n        self.user = try container.decode(User.self, forKey: .user)\n        if let rlimits = try container.decodeIfPresent([POSIXRlimit].self, forKey: .rlimits) {\n            self.rlimits = rlimits\n        }\n        if let args = try container.decodeIfPresent([String].self, forKey: .args) {\n            self.args = args\n        }\n        if let terminal = try container.decodeIfPresent(Bool.self, forKey: .terminal) {\n            self.terminal = terminal\n        }\n    }\n}\n\npublic struct LinuxCapabilities: Codable, Sendable {\n    public var bounding: [String]?\n    public var effective: [String]?\n    public var inheritable: [String]?\n    public var permitted: [String]?\n    public var ambient: [String]?\n\n    enum CodingKeys: String, CodingKey {\n        case bounding\n        case effective\n        case inheritable\n        case permitted\n        case ambient\n    }\n\n    public init(\n        bounding: [String]? = nil,\n        effective: [String]? = nil,\n        inheritable: [String]? = nil,\n        permitted: [String]? = nil,\n        ambient: [String]? = nil\n    ) {\n        self.bounding = bounding\n        self.effective = effective\n        self.inheritable = inheritable\n        self.permitted = permitted\n        self.ambient = ambient\n    }\n\n    public init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        self.bounding = try container.decodeIfPresent([String].self, forKey: .bounding)\n        self.effective = try container.decodeIfPresent([String].self, forKey: .effective)\n        self.inheritable = try container.decodeIfPresent([String].self, forKey: .inheritable)\n        self.permitted = try container.decodeIfPresent([String].self, forKey: .permitted)\n        self.ambient = try container.decodeIfPresent([String].self, forKey: .ambient)\n    }\n\n    public func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encodeIfPresent(bounding, forKey: .bounding)\n        try container.encodeIfPresent(effective, forKey: .effective)\n        try container.encodeIfPresent(inheritable, forKey: .inheritable)\n        try container.encodeIfPresent(permitted, forKey: .permitted)\n        try container.encodeIfPresent(ambient, forKey: .ambient)\n    }\n}\n\npublic struct Box: Codable, Sendable {\n    var height, width: UInt\n\n    public init(height: UInt, width: UInt) {\n        self.height = height\n        self.width = width\n    }\n}\n\npublic struct User: Codable, Sendable {\n    public var uid: UInt32\n    public var gid: UInt32\n    public var umask: UInt32?\n    public var additionalGids: [UInt32]\n    public var username: String\n\n    public enum CodingKeys: String, CodingKey {\n        case uid\n        case gid\n        case umask\n        case additionalGids\n        case username\n    }\n\n    public init(\n        uid: UInt32 = 0,\n        gid: UInt32 = 0,\n        umask: UInt32? = nil,\n        additionalGids: [UInt32] = [],\n        username: String = \"\"\n    ) {\n        self.uid = uid\n        self.gid = gid\n        self.umask = umask\n        self.additionalGids = additionalGids\n        self.username = username\n    }\n\n    public init(from decoder: Decoder) throws {\n        self.init()\n\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        self.uid = try container.decode(UInt32.self, forKey: .uid)\n        self.gid = try container.decode(UInt32.self, forKey: .gid)\n        self.umask = try container.decodeIfPresent(UInt32.self, forKey: .umask)\n        if let additionalGids = try container.decodeIfPresent([UInt32].self, forKey: .additionalGids) {\n            self.additionalGids = additionalGids\n        }\n        if let username = try container.decodeIfPresent(String.self, forKey: .username) {\n            self.username = username\n        }\n    }\n}\n\npublic struct Root: Codable, Sendable {\n    public var path: String\n    public var readonly: Bool\n\n    public enum CodingKeys: String, CodingKey {\n        case path\n        case readonly\n    }\n\n    public init(path: String, readonly: Bool) {\n        self.path = path\n        self.readonly = readonly\n    }\n\n    public init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        self.path = try container.decode(String.self, forKey: .path)\n        self.readonly = try container.decodeIfPresent(Bool.self, forKey: .readonly) ?? false\n    }\n}\n\npublic struct Mount: Codable, Sendable {\n    public var type: String\n    public var source: String\n    public var destination: String\n    public var options: [String]\n\n    public var uidMappings: [LinuxIDMapping]?\n    public var gidMappings: [LinuxIDMapping]?\n\n    public enum CodingKeys: String, CodingKey {\n        case type\n        case source\n        case destination\n        case options\n        case uidMappings\n        case gidMappings\n    }\n\n    public init(\n        type: String = \"\",\n        source: String = \"\",\n        destination: String,\n        options: [String] = [],\n        uidMappings: [LinuxIDMapping]? = nil,\n        gidMappings: [LinuxIDMapping]? = nil\n    ) {\n        self.destination = destination\n        self.type = type\n        self.source = source\n        self.options = options\n        self.uidMappings = uidMappings\n        self.gidMappings = gidMappings\n    }\n\n    public init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        self.type = try container.decodeIfPresent(String.self, forKey: .type) ?? \"\"\n        self.source = try container.decodeIfPresent(String.self, forKey: .source) ?? \"\"\n        self.destination = try container.decode(String.self, forKey: .destination)\n        self.options = try container.decodeIfPresent([String].self, forKey: .options) ?? []\n        self.uidMappings = try container.decodeIfPresent([LinuxIDMapping].self, forKey: .uidMappings)\n        self.gidMappings = try container.decodeIfPresent([LinuxIDMapping].self, forKey: .gidMappings)\n    }\n\n    public func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(type, forKey: .type)\n        try container.encode(source, forKey: .source)\n        try container.encode(destination, forKey: .destination)\n        try container.encode(options, forKey: .options)\n        try container.encodeIfPresent(uidMappings, forKey: .uidMappings)\n        try container.encodeIfPresent(gidMappings, forKey: .gidMappings)\n    }\n}\n\npublic struct Hook: Codable, Sendable {\n    public var path: String\n    public var args: [String]\n    public var env: [String]\n    public var timeout: Int?\n\n    public init(path: String, args: [String], env: [String], timeout: Int?) {\n        self.path = path\n        self.args = args\n        self.env = env\n        self.timeout = timeout\n    }\n}\n\npublic struct Hooks: Codable, Sendable {\n    public var prestart: [Hook]\n    public var createRuntime: [Hook]\n    public var createContainer: [Hook]\n    public var startContainer: [Hook]\n    public var poststart: [Hook]\n    public var poststop: [Hook]\n\n    public init(\n        prestart: [Hook],\n        createRuntime: [Hook],\n        createContainer: [Hook],\n        startContainer: [Hook],\n        poststart: [Hook],\n        poststop: [Hook]\n    ) {\n        self.prestart = prestart\n        self.createRuntime = createRuntime\n        self.createContainer = createContainer\n        self.startContainer = startContainer\n        self.poststart = poststart\n        self.poststop = poststop\n    }\n}\n\npublic struct Linux: Codable, Sendable {\n    public var uidMappings: [LinuxIDMapping]\n    public var gidMappings: [LinuxIDMapping]\n    public var sysctl: [String: String]?\n    public var resources: LinuxResources?\n    public var cgroupsPath: String\n    public var namespaces: [LinuxNamespace]\n    public var devices: [LinuxDevice]\n    public var seccomp: LinuxSeccomp?\n    public var rootfsPropagation: String\n    public var maskedPaths: [String]\n    public var readonlyPaths: [String]\n    public var mountLabel: String\n    public var personality: LinuxPersonality?\n\n    public enum CodingKeys: String, CodingKey {\n        case uidMappings\n        case gidMappings\n        case sysctl\n        case resources\n        case cgroupsPath\n        case namespaces\n        case devices\n        case seccomp\n        case rootfsPropagation\n        case maskedPaths\n        case readonlyPaths\n        case mountLabel\n        case personality\n    }\n\n    public init(\n        uidMappings: [LinuxIDMapping] = [],\n        gidMappings: [LinuxIDMapping] = [],\n        sysctl: [String: String]? = nil,\n        resources: LinuxResources? = nil,\n        cgroupsPath: String = \"\",\n        namespaces: [LinuxNamespace] = [],\n        devices: [LinuxDevice] = [],\n        seccomp: LinuxSeccomp? = nil,\n        rootfsPropagation: String = \"\",\n        maskedPaths: [String] = [],\n        readonlyPaths: [String] = [],\n        mountLabel: String = \"\",\n        personality: LinuxPersonality? = nil\n    ) {\n        self.uidMappings = uidMappings\n        self.gidMappings = gidMappings\n        self.sysctl = sysctl\n        self.resources = resources\n        self.cgroupsPath = cgroupsPath\n        self.namespaces = namespaces\n        self.devices = devices\n        self.seccomp = seccomp\n        self.rootfsPropagation = rootfsPropagation\n        self.maskedPaths = maskedPaths\n        self.readonlyPaths = readonlyPaths\n        self.mountLabel = mountLabel\n        self.personality = personality\n    }\n\n    public init(from decoder: Decoder) throws {\n        self.init()\n\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        if let uidMappings = try container.decodeIfPresent([LinuxIDMapping].self, forKey: .uidMappings) {\n            self.uidMappings = uidMappings\n        }\n        if let gidMappings = try container.decodeIfPresent([LinuxIDMapping].self, forKey: .gidMappings) {\n            self.gidMappings = gidMappings\n        }\n        self.sysctl = try container.decodeIfPresent([String: String].self, forKey: .sysctl)\n        self.resources = try container.decodeIfPresent(LinuxResources.self, forKey: .resources)\n        if let cgroupsPath = try container.decodeIfPresent(String.self, forKey: .cgroupsPath) {\n            self.cgroupsPath = cgroupsPath\n        }\n        if let namespaces = try container.decodeIfPresent([LinuxNamespace].self, forKey: .namespaces) {\n            self.namespaces = namespaces\n        }\n        if let devices = try container.decodeIfPresent([LinuxDevice].self, forKey: .devices) {\n            self.devices = devices\n        }\n        self.seccomp = try container.decodeIfPresent(LinuxSeccomp.self, forKey: .seccomp)\n        if let rootfsPropagation = try container.decodeIfPresent(String.self, forKey: .rootfsPropagation) {\n            self.rootfsPropagation = rootfsPropagation\n        }\n        if let maskedPaths = try container.decodeIfPresent([String].self, forKey: .maskedPaths) {\n            self.maskedPaths = maskedPaths\n        }\n        if let readonlyPaths = try container.decodeIfPresent([String].self, forKey: .readonlyPaths) {\n            self.readonlyPaths = readonlyPaths\n        }\n        if let mountLabel = try container.decodeIfPresent(String.self, forKey: .mountLabel) {\n            self.mountLabel = mountLabel\n        }\n        self.personality = try container.decodeIfPresent(LinuxPersonality.self, forKey: .personality)\n    }\n}\n\npublic struct LinuxNamespace: Codable, Sendable {\n    public var type: LinuxNamespaceType\n    public var path: String\n\n    public init(type: LinuxNamespaceType, path: String = \"\") {\n        self.type = type\n        self.path = path\n    }\n}\n\npublic enum LinuxNamespaceType: String, Codable, Sendable {\n    case pid\n    case network\n    case uts\n    case mount\n    case ipc\n    case user\n    case cgroup\n}\n\npublic struct LinuxIDMapping: Codable, Sendable {\n    public var containerID: UInt32\n    public var hostID: UInt32\n    public var size: UInt32\n\n    public init(containerID: UInt32, hostID: UInt32, size: UInt32) {\n        self.containerID = containerID\n        self.hostID = hostID\n        self.size = size\n    }\n}\n\npublic struct POSIXRlimit: Codable, Sendable {\n    public var type: String\n    public var hard: UInt64\n    public var soft: UInt64\n\n    public init(type: String, hard: UInt64, soft: UInt64) {\n        self.type = type\n        self.hard = hard\n        self.soft = soft\n    }\n}\n\npublic struct LinuxHugepageLimit: Codable, Sendable {\n    public var pagesize: String\n    public var limit: UInt64\n\n    public init(pagesize: String, limit: UInt64) {\n        self.pagesize = pagesize\n        self.limit = limit\n    }\n}\n\npublic struct LinuxInterfacePriority: Codable, Sendable {\n    public var name: String\n    public var priority: UInt32\n\n    public init(name: String, priority: UInt32) {\n        self.name = name\n        self.priority = priority\n    }\n}\n\npublic struct LinuxBlockIODevice: Codable, Sendable {\n    public var major: Int64\n    public var minor: Int64\n\n    public init(major: Int64, minor: Int64) {\n        self.major = major\n        self.minor = minor\n    }\n}\n\npublic struct LinuxWeightDevice: Codable, Sendable {\n    public var major: Int64\n    public var minor: Int64\n    public var weight: UInt16?\n    public var leafWeight: UInt16?\n\n    public init(major: Int64, minor: Int64, weight: UInt16?, leafWeight: UInt16?) {\n        self.major = major\n        self.minor = minor\n        self.weight = weight\n        self.leafWeight = leafWeight\n    }\n}\n\npublic struct LinuxThrottleDevice: Codable, Sendable {\n    public var major: Int64\n    public var minor: Int64\n    public var rate: UInt64\n\n    public init(major: Int64, minor: Int64, rate: UInt64) {\n        self.major = major\n        self.minor = minor\n        self.rate = rate\n    }\n}\n\npublic struct LinuxBlockIO: Codable, Sendable {\n    public var weight: UInt16?\n    public var leafWeight: UInt16?\n    public var weightDevice: [LinuxWeightDevice]\n    public var throttleReadBpsDevice: [LinuxThrottleDevice]\n    public var throttleWriteBpsDevice: [LinuxThrottleDevice]\n    public var throttleReadIOPSDevice: [LinuxThrottleDevice]\n    public var throttleWriteIOPSDevice: [LinuxThrottleDevice]\n\n    public init(\n        weight: UInt16?,\n        leafWeight: UInt16?,\n        weightDevice: [LinuxWeightDevice],\n        throttleReadBpsDevice: [LinuxThrottleDevice],\n        throttleWriteBpsDevice: [LinuxThrottleDevice],\n        throttleReadIOPSDevice: [LinuxThrottleDevice],\n        throttleWriteIOPSDevice: [LinuxThrottleDevice]\n    ) {\n        self.weight = weight\n        self.leafWeight = leafWeight\n        self.weightDevice = weightDevice\n        self.throttleReadBpsDevice = throttleReadBpsDevice\n        self.throttleWriteBpsDevice = throttleWriteBpsDevice\n        self.throttleReadIOPSDevice = throttleReadIOPSDevice\n        self.throttleWriteIOPSDevice = throttleWriteIOPSDevice\n    }\n}\n\npublic struct LinuxMemory: Codable, Sendable {\n    public var limit: Int64?\n    public var reservation: Int64?\n    public var swap: Int64?\n    public var kernel: Int64?\n    public var kernelTCP: Int64?\n    public var swappiness: UInt64?\n    public var disableOOMKiller: Bool?\n    public var useHierarchy: Bool?\n    public var checkBeforeUpdate: Bool?\n\n    public init(\n        limit: Int64? = nil,\n        reservation: Int64? = nil,\n        swap: Int64? = nil,\n        kernel: Int64? = nil,\n        kernelTCP: Int64? = nil,\n        swappiness: UInt64? = nil,\n        disableOOMKiller: Bool? = nil,\n        useHierarchy: Bool? = nil,\n        checkBeforeUpdate: Bool? = nil\n    ) {\n        self.limit = limit\n        self.reservation = reservation\n        self.swap = swap\n        self.kernel = kernel\n        self.kernelTCP = kernelTCP\n        self.swappiness = swappiness\n        self.disableOOMKiller = disableOOMKiller\n        self.useHierarchy = useHierarchy\n        self.checkBeforeUpdate = checkBeforeUpdate\n    }\n}\n\npublic struct LinuxCPU: Codable, Sendable {\n    public var shares: UInt64?\n    public var quota: Int64?\n    public var burst: UInt64?\n    public var period: UInt64?\n    public var realtimeRuntime: Int64?\n    public var realtimePeriod: Int64?\n    public var cpus: String\n    public var mems: String\n    public var idle: Int64?\n\n    public init(\n        shares: UInt64? = nil,\n        quota: Int64? = nil,\n        burst: UInt64? = nil,\n        period: UInt64? = nil,\n        realtimeRuntime: Int64? = nil,\n        realtimePeriod: Int64? = nil,\n        cpus: String = \"\",\n        mems: String = \"\",\n        idle: Int64? = nil\n    ) {\n        self.shares = shares\n        self.quota = quota\n        self.burst = burst\n        self.period = period\n        self.realtimeRuntime = realtimeRuntime\n        self.realtimePeriod = realtimePeriod\n        self.cpus = cpus\n        self.mems = mems\n        self.idle = idle\n    }\n}\n\npublic struct LinuxPids: Codable, Sendable {\n    public var limit: Int64\n\n    public init(limit: Int64) {\n        self.limit = limit\n    }\n}\n\npublic struct LinuxNetwork: Codable, Sendable {\n    public var classID: UInt32?\n    public var priorities: [LinuxInterfacePriority]\n\n    public init(classID: UInt32?, priorities: [LinuxInterfacePriority]) {\n        self.classID = classID\n        self.priorities = priorities\n    }\n}\n\npublic struct LinuxRdma: Codable, Sendable {\n    public var hcsHandles: UInt32?\n    public var hcaObjects: UInt32?\n\n    public init(hcsHandles: UInt32?, hcaObjects: UInt32?) {\n        self.hcsHandles = hcsHandles\n        self.hcaObjects = hcaObjects\n    }\n}\n\npublic struct LinuxResources: Codable, Sendable {\n    public var devices: [LinuxDeviceCgroup]\n    public var memory: LinuxMemory?\n    public var cpu: LinuxCPU?\n    public var pids: LinuxPids?\n    public var blockIO: LinuxBlockIO?\n    public var hugepageLimits: [LinuxHugepageLimit]\n    public var network: LinuxNetwork?\n    public var rdma: [String: LinuxRdma]?\n    public var unified: [String: String]?\n\n    public init(\n        devices: [LinuxDeviceCgroup] = [],\n        memory: LinuxMemory? = nil,\n        cpu: LinuxCPU? = nil,\n        pids: LinuxPids? = nil,\n        blockIO: LinuxBlockIO? = nil,\n        hugepageLimits: [LinuxHugepageLimit] = [],\n        network: LinuxNetwork? = nil,\n        rdma: [String: LinuxRdma]? = nil,\n        unified: [String: String] = [:]\n    ) {\n        self.devices = devices\n        self.memory = memory\n        self.cpu = cpu\n        self.pids = pids\n        self.blockIO = blockIO\n        self.hugepageLimits = hugepageLimits\n        self.network = network\n        self.rdma = rdma\n        self.unified = unified\n    }\n}\n\npublic struct LinuxDevice: Codable, Sendable {\n    public var path: String\n    public var type: String\n    public var major: Int64\n    public var minor: Int64\n    public var fileMode: UInt32?\n    public var uid: UInt32?\n    public var gid: UInt32?\n\n    public init(\n        path: String,\n        type: String,\n        major: Int64,\n        minor: Int64,\n        fileMode: UInt32?,\n        uid: UInt32?,\n        gid: UInt32?\n    ) {\n        self.path = path\n        self.type = type\n        self.major = major\n        self.minor = minor\n        self.fileMode = fileMode\n        self.uid = uid\n        self.gid = gid\n    }\n}\n\npublic struct LinuxDeviceCgroup: Codable, Sendable {\n    public var allow: Bool\n    public var type: String\n    public var major: Int64?\n    public var minor: Int64?\n    public var access: String?\n\n    public init(allow: Bool, type: String, major: Int64?, minor: Int64?, access: String?) {\n        self.allow = allow\n        self.type = type\n        self.major = major\n        self.minor = minor\n        self.access = access\n    }\n}\n\npublic enum LinuxPersonalityDomain: String, Codable, Sendable {\n    case perLinux = \"LINUX\"\n    case perLinux32 = \"LINUX32\"\n}\n\npublic struct LinuxPersonality: Codable, Sendable {\n    public var domain: LinuxPersonalityDomain\n    public var flags: [String]\n\n    public init(domain: LinuxPersonalityDomain, flags: [String]) {\n        self.domain = domain\n        self.flags = flags\n    }\n}\n\npublic struct LinuxSeccomp: Codable, Sendable {\n    public var defaultAction: LinuxSeccompAction\n    public var defaultErrnoRet: UInt?\n    public var architectures: [Arch]\n    public var flags: [LinuxSeccompFlag]\n    public var listenerPath: String\n    public var listenerMetadata: String\n    public var syscalls: [LinuxSyscall]\n\n    public init(\n        defaultAction: LinuxSeccompAction,\n        defaultErrnoRet: UInt?,\n        architectures: [Arch],\n        flags: [LinuxSeccompFlag],\n        listenerPath: String,\n        listenerMetadata: String,\n        syscalls: [LinuxSyscall]\n    ) {\n        self.defaultAction = defaultAction\n        self.defaultErrnoRet = defaultErrnoRet\n        self.architectures = architectures\n        self.flags = flags\n        self.listenerPath = listenerPath\n        self.listenerMetadata = listenerMetadata\n        self.syscalls = syscalls\n    }\n}\n\npublic enum LinuxSeccompFlag: String, Codable, Sendable {\n    case flagLog = \"SECCOMP_FILTER_FLAG_LOG\"\n    case flagSpecAllow = \"SECCOMP_FILTER_FLAG_SPEC_ALLOW\"\n    case flagWaitKillableRecv = \"SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV\"\n}\n\npublic enum Arch: String, Codable, Sendable {\n    case archX86 = \"SCMP_ARCH_X86\"\n    case archX86_64 = \"SCMP_ARCH_X86_64\"\n    case archX32 = \"SCMP_ARCH_X32\"\n    case archARM = \"SCMP_ARCH_ARM\"\n    case archAARCH64 = \"SCMP_ARCH_AARCH64\"\n    case archMIPS = \"SCMP_ARCH_MIPS\"\n    case archMIPS64 = \"SCMP_ARCH_MIPS64\"\n    case archMIPS64N32 = \"SCMP_ARCH_MIPS64N32\"\n    case archMIPSEL = \"SCMP_ARCH_MIPSEL\"\n    case archMIPSEL64 = \"SCMP_ARCH_MIPSEL64\"\n    case archMIPSEL64N32 = \"SCMP_ARCH_MIPSEL64N32\"\n    case archPPC = \"SCMP_ARCH_PPC\"\n    case archPPC64 = \"SCMP_ARCH_PPC64\"\n    case archPPC64LE = \"SCMP_ARCH_PPC64LE\"\n    case archS390 = \"SCMP_ARCH_S390\"\n    case archS390X = \"SCMP_ARCH_S390X\"\n    case archPARISC = \"SCMP_ARCH_PARISC\"\n    case archPARISC64 = \"SCMP_ARCH_PARISC64\"\n    case archRISCV64 = \"SCMP_ARCH_RISCV64\"\n}\n\npublic enum LinuxSeccompAction: String, Codable, Sendable {\n    case actKill = \"SCMP_ACT_KILL\"\n    case actKillProcess = \"SCMP_ACT_KILL_PROCESS\"\n    case actKillThread = \"SCMP_ACT_KILL_THREAD\"\n    case actTrap = \"SCMP_ACT_TRAP\"\n    case actErrno = \"SCMP_ACT_ERRNO\"\n    case actTrace = \"SCMP_ACT_TRACE\"\n    case actAllow = \"SCMP_ACT_ALLOW\"\n    case actLog = \"SCMP_ACT_LOG\"\n    case actNotify = \"SCMP_ACT_NOTIFY\"\n}\n\npublic enum LinuxSeccompOperator: String, Codable, Sendable {\n    case opNotEqual = \"SCMP_CMP_NE\"\n    case opLessThan = \"SCMP_CMP_LT\"\n    case opLessEqual = \"SCMP_CMP_LE\"\n    case opEqualTo = \"SCMP_CMP_EQ\"\n    case opGreaterEqual = \"SCMP_CMP_GE\"\n    case opGreaterThan = \"SCMP_CMP_GT\"\n    case opMaskedEqual = \"SCMP_CMP_MASKED_EQ\"\n}\n\npublic struct LinuxSeccompArg: Codable, Sendable {\n    public var index: UInt\n    public var value: UInt64\n    public var valueTwo: UInt64\n    public var op: LinuxSeccompOperator\n\n    public init(index: UInt, value: UInt64, valueTwo: UInt64, op: LinuxSeccompOperator) {\n        self.index = index\n        self.value = value\n        self.valueTwo = valueTwo\n        self.op = op\n    }\n}\n\npublic struct LinuxSyscall: Codable, Sendable {\n    public var names: [String]\n    public var action: LinuxSeccompAction\n    public var errnoRet: UInt?\n    public var args: [LinuxSeccompArg]\n\n    public init(\n        names: [String],\n        action: LinuxSeccompAction,\n        errnoRet: UInt?,\n        args: [LinuxSeccompArg]\n    ) {\n        self.names = names\n        self.action = action\n        self.errnoRet = errnoRet\n        self.args = args\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/State.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\npublic enum ContainerState: String, Codable, Sendable {\n    case creating\n    case created\n    case running\n    case stopped\n}\n\npublic struct State: Codable, Sendable {\n    public init(\n        version: String,\n        id: String,\n        status: ContainerState,\n        pid: Int,\n        bundle: String,\n        annotations: [String: String]?\n    ) {\n        self.ociVersion = version\n        self.id = id\n        self.status = status\n        self.pid = pid\n        self.bundle = bundle\n        self.annotations = annotations\n    }\n\n    public init(instance: State) {\n        self.ociVersion = instance.ociVersion\n        self.id = instance.id\n        self.status = instance.status\n        self.pid = instance.pid\n        self.bundle = instance.bundle\n        self.annotations = instance.annotations\n    }\n\n    public let ociVersion: String\n    public let id: String\n    public let status: ContainerState\n    public let pid: Int\n    public let bundle: String\n    public var annotations: [String: String]?\n}\n\npublic let seccompFdName: String = \"seccompFd\"\n\npublic struct ContainerProcessState: Codable, Sendable {\n    public init(version: String, fds: [String], pid: Int, metadata: String, state: State) {\n        self.ociVersion = version\n        self.fds = fds\n        self.pid = pid\n        self.metadata = metadata\n        self.state = state\n    }\n\n    public init(instance: ContainerProcessState) {\n        self.ociVersion = instance.ociVersion\n        self.fds = instance.fds\n        self.pid = instance.pid\n        self.metadata = instance.metadata\n        self.state = instance.state\n    }\n\n    public let ociVersion: String\n    public var fds: [String]\n    public let pid: Int\n    public let metadata: String\n    public let state: State\n}\n"
  },
  {
    "path": "Sources/ContainerizationOCI/Version.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\npublic struct RuntimeSpecVersion: Sendable {\n    public let major, minor, patch: Int\n    public let dev: String\n\n    public static let current = RuntimeSpecVersion(\n        major: 1,\n        minor: 0,\n        patch: 2,\n        dev: \"-dev\"\n    )\n\n    public init(major: Int, minor: Int, patch: Int, dev: String) {\n        self.major = major\n        self.minor = minor\n        self.patch = patch\n        self.dev = dev\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/AsyncSignalHandler.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Synchronization\n\n/// Async friendly wrapper around `DispatchSourceSignal`. Provides an `AsyncStream`\n/// interface to get notified of received signals.\npublic final class AsyncSignalHandler: Sendable {\n    /// An async stream that returns the signal that was caught, if ever.\n    public var signals: AsyncStream<Int32> {\n        let (stream, cont) = AsyncStream.makeStream(of: Int32.self)\n        self.state.withLock {\n            $0.conts.append(cont)\n        }\n        cont.onTermination = { @Sendable _ in\n            self.cancel()\n        }\n        return stream\n    }\n\n    /// Cancel every AsyncStream of signals, as well as the underlying\n    /// DispatchSignalSource's for each registered signal.\n    public func cancel() {\n        self.state.withLock {\n            if $0.conts.isEmpty {\n                return\n            }\n\n            for cont in $0.conts {\n                cont.finish()\n            }\n            for source in $0.sources {\n                source.cancel()\n            }\n            $0.conts.removeAll()\n            $0.sources.removeAll()\n        }\n    }\n\n    struct State: Sendable {\n        var conts: [AsyncStream<Int32>.Continuation] = []\n        // `sources` isn't used concurrently.\n        nonisolated(unsafe) var sources: [any DispatchSourceSignal] = []\n    }\n\n    // We keep a reference to the continuation object that is created for\n    // our AsyncStream and tell our signal handler to yield a value to it\n    // returning a value to the consumer\n    private func handler(_ sig: Int32) {\n        self.state.withLock {\n            for cont in $0.conts {\n                cont.yield(sig)\n            }\n        }\n    }\n\n    private let state: Mutex<State> = .init(State())\n\n    /// Create a new `AsyncSignalHandler` for the list of given signals `notify`.\n    /// The default signal handlers for these signals are removed and async handlers\n    /// added in their place. The async signal handlers that are installed simply\n    /// yield to a stream if and when a signal is caught.\n    public static func create(notify on: [Int32]) -> AsyncSignalHandler {\n        let out = AsyncSignalHandler()\n        var sources = [any DispatchSourceSignal]()\n        for sig in on {\n            signal(sig, SIG_IGN)\n            let source = DispatchSource.makeSignalSource(signal: sig)\n            source.setEventHandler {\n                out.handler(sig)\n            }\n            source.resume()\n            // Retain a reference to our signal sources so that they\n            // do not go out of scope.\n            sources.append(source)\n        }\n        out.state.withLock { $0.sources = sources }\n        return out\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/BinaryInteger+Extensions.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nextension BinaryInteger {\n    private func toUnsignedMemoryAmount(_ amount: UInt64) -> UInt64 {\n        guard self >= 0 else {\n            fatalError(\"encountered negative number during conversion to memory amount\")\n        }\n        let val = UInt64(self)\n        let (newVal, overflow) = val.multipliedReportingOverflow(by: amount)\n        guard !overflow else {\n            fatalError(\"UInt64 overflow when converting to memory amount\")\n        }\n        return newVal\n    }\n\n    public func kib() -> UInt64 {\n        self.toUnsignedMemoryAmount(1 << 10)\n    }\n\n    public func mib() -> UInt64 {\n        self.toUnsignedMemoryAmount(1 << 20)\n    }\n\n    public func gib() -> UInt64 {\n        self.toUnsignedMemoryAmount(1 << 30)\n    }\n\n    public func tib() -> UInt64 {\n        self.toUnsignedMemoryAmount(1 << 40)\n    }\n\n    public func pib() -> UInt64 {\n        self.toUnsignedMemoryAmount(1 << 50)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/Command.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport CShim\nimport Foundation\nimport Synchronization\n\n#if canImport(Darwin)\nimport Darwin\nprivate let _kill = Darwin.kill\n#elseif canImport(Musl)\nimport Musl\nprivate let _kill = Musl.kill\n#elseif canImport(Glibc)\nimport Glibc\nprivate let _kill = Glibc.kill\n#endif\n\n/// Use a command to run an executable.\npublic struct Command: Sendable {\n    /// Path to the executable binary.\n    public var executable: String\n    /// Arguments provided to the binary.\n    public var arguments: [String]\n    /// Environment variables for the process.\n    public var environment: [String]\n    /// The directory where the process should execute.\n    public var directory: String?\n    /// Additional files to pass to the process.\n    public var extraFiles: [FileHandle]\n    /// The standard input.\n    public var stdin: FileHandle?\n    /// The standard output.\n    public var stdout: FileHandle?\n    /// The standard error.\n    public var stderr: FileHandle?\n\n    private let state: State\n\n    /// System level attributes to set on the process.\n    public struct Attrs: Sendable {\n        /// Set pgroup for the new process.\n        public var setPGroup: Bool\n        /// Make the new process group the foreground process group (requires setPGroup).\n        public var setForegroundPGroup: Bool\n        /// Inherit the real uid/gid of the parent.\n        public var resetIDs: Bool\n        /// Reset the child's signal handlers to the default.\n        public var setSignalDefault: Bool\n        /// The initial signal mask for the process.\n        public var signalMask: UInt32\n        /// Create a new session for the process.\n        public var setsid: Bool\n        /// Set the controlling terminal for the process to fd 0.\n        public var setctty: Bool\n        /// Set the process user ID.\n        public var uid: UInt32?\n        /// Set the process group ID.\n        public var gid: UInt32?\n        /// Signal to send when parent process dies (Linux only).\n        public var pdeathSignal: Int32?\n\n        public init(\n            setPGroup: Bool = false,\n            setForegroundPGroup: Bool = false,\n            resetIDs: Bool = false,\n            setSignalDefault: Bool = true,\n            signalMask: UInt32 = 0,\n            setsid: Bool = false,\n            setctty: Bool = false,\n            uid: UInt32? = nil,\n            gid: UInt32? = nil,\n            pdeathSignal: Int32? = nil\n        ) {\n            self.setPGroup = setPGroup\n            self.setForegroundPGroup = setForegroundPGroup\n            self.resetIDs = resetIDs\n            self.setSignalDefault = setSignalDefault\n            self.signalMask = signalMask\n            self.setsid = setsid\n            self.setctty = setctty\n            self.uid = uid\n            self.gid = gid\n            self.pdeathSignal = pdeathSignal\n        }\n    }\n\n    private final class State: Sendable {\n        let pid: Atomic<pid_t> = Atomic(-1)\n    }\n\n    /// Attributes to set on the process.\n    public var attrs = Attrs()\n\n    /// System level process identifier.\n    public var pid: Int32 { self.state.pid.load(ordering: .acquiring) }\n\n    public init(\n        _ executable: String,\n        arguments: [String] = [],\n        environment: [String] = environment(),\n        directory: String? = nil,\n        extraFiles: [FileHandle] = []\n    ) {\n        self.executable = executable\n        self.arguments = arguments\n        self.environment = environment\n        self.extraFiles = extraFiles\n        self.directory = directory\n        self.state = State()\n    }\n\n    public static func environment() -> [String] {\n        ProcessInfo.processInfo.environment\n            .map { \"\\($0)=\\($1)\" }\n    }\n}\n\nextension Command {\n    public enum Error: Swift.Error, CustomStringConvertible {\n        case processRunning\n\n        public var description: String {\n            switch self {\n            case .processRunning:\n                return \"the process is already running\"\n            }\n        }\n    }\n}\n\nextension Command {\n    @discardableResult\n    public func kill(_ signal: Int32) -> Int32? {\n        let pid = self.pid\n        guard pid > 0 else {\n            return nil\n        }\n        return _kill(pid, signal)\n    }\n}\n\nextension Command {\n    /// Start the process.\n    public func start() throws {\n        guard self.pid == -1 else {\n            throw Error.processRunning\n        }\n        let child = try execute()\n        self.state.pid.store(child, ordering: .releasing)\n    }\n\n    /// Wait for the process to exit and return the exit status.\n    @discardableResult\n    public func wait() throws -> Int32 {\n        var rus = rusage()\n        var ws = Int32()\n\n        let pid = self.pid\n        guard pid > 0 else {\n            return -1\n        }\n\n        let result = wait4(pid, &ws, 0, &rus)\n        guard result == pid else {\n            throw POSIXError(.init(rawValue: errno)!)\n        }\n        return Self.toExitStatus(ws)\n    }\n\n    private func execute() throws -> pid_t {\n        var attrs = exec_command_attrs()\n        exec_command_attrs_init(&attrs)\n\n        let set = try createFileset()\n        defer {\n            for nullHandle in set.nullHandles {\n                try? nullHandle.close()\n            }\n        }\n        var fds = [Int32](repeating: 0, count: set.handles.count)\n        for (i, handle) in set.handles.enumerated() {\n            fds[i] = handle.fileDescriptor\n        }\n\n        attrs.setsid = self.attrs.setsid ? 1 : 0\n        attrs.setctty = self.attrs.setctty ? 1 : 0\n        attrs.setpgid = self.attrs.setPGroup ? 1 : 0\n        attrs.setfgpgrp = self.attrs.setForegroundPGroup ? 1 : 0\n\n        var cwdPath: UnsafeMutablePointer<CChar>?\n        if let chdir = self.directory {\n            cwdPath = strdup(chdir)\n        }\n        defer {\n            if let cwdPath {\n                free(cwdPath)\n            }\n        }\n\n        if let uid = self.attrs.uid {\n            attrs.uid = uid\n        }\n        if let gid = self.attrs.gid {\n            attrs.gid = gid\n        }\n\n        if let pdeathSignal = self.attrs.pdeathSignal {\n            attrs.pdeathSignal = pdeathSignal\n        }\n\n        var pid: pid_t = 0\n        var argv = ([executable] + arguments).map { strdup($0) } + [nil]\n        defer {\n            for arg in argv where arg != nil {\n                free(arg)\n            }\n        }\n\n        let env = environment.map { strdup($0) } + [nil]\n        defer {\n            for e in env where e != nil {\n                free(e)\n            }\n        }\n\n        let result = fds.withUnsafeBufferPointer { file_handles in\n            exec_command(\n                &pid,\n                argv[0],\n                &argv,\n                env,\n                file_handles.baseAddress!, Int32(file_handles.count),\n                cwdPath ?? nil,\n                &attrs)\n        }\n        guard result == 0 else {\n            throw POSIXError(.init(rawValue: errno)!)\n        }\n\n        return pid\n    }\n\n    /// Create a posix_spawn file actions set of fds to pass to the new process\n    private func createFileset() throws -> (nullHandles: [FileHandle], handles: [FileHandle]) {\n        // grab dev null handles for different purposes\n        let nullRead = try openDevNull(flags: O_RDONLY)\n        let nullWrite = try openDevNull(flags: O_WRONLY)\n        var files = [FileHandle]()\n        files.append(stdin ?? nullRead)\n        files.append(stdout ?? nullWrite)\n        files.append(stderr ?? nullWrite)\n        files.append(contentsOf: extraFiles)\n        return (nullHandles: [nullRead, nullWrite], handles: files)\n    }\n\n    /// Returns a file handle to /dev/null with the specified flags.\n    private func openDevNull(flags: Int32) throws -> FileHandle {\n        let fd = open(\"/dev/null\", flags, 0)\n        guard fd >= 0 else {\n            throw POSIXError(.init(rawValue: errno)!)\n        }\n        return FileHandle(fileDescriptor: fd, closeOnDealloc: false)\n    }\n}\n\nextension Command {\n    private static let signalOffset: Int32 = 128\n\n    private static let shift: Int32 = 8\n    private static let mask: Int32 = 0x7F\n    private static let stopped: Int32 = 0x7F\n    private static let exited: Int32 = 0x00\n\n    static func signaled(_ ws: Int32) -> Bool {\n        ws & mask != stopped && ws & mask != exited\n    }\n\n    static func exited(_ ws: Int32) -> Bool {\n        ws & mask == exited\n    }\n\n    static func exitStatus(_ ws: Int32) -> Int32 {\n        let r: Int32\n        #if os(Linux)\n        r = ws >> shift & 0xFF\n        #else\n        r = ws >> shift\n        #endif\n        return r\n    }\n\n    public static func toExitStatus(_ ws: Int32) -> Int32 {\n        if signaled(ws) {\n            // We use the offset as that is how existing container\n            // runtimes minic bash for the status when signaled.\n            return Int32(Self.signalOffset + ws & mask)\n        }\n        if exited(ws) {\n            return exitStatus(ws)\n        }\n        return ws\n    }\n\n}\n\nprivate func WIFEXITED(_ status: Int32) -> Bool {\n    _WSTATUS(status) == 0\n}\n\nprivate func _WSTATUS(_ status: Int32) -> Int32 {\n    status & 0x7f\n}\n\nprivate func WIFSIGNALED(_ status: Int32) -> Bool {\n    (_WSTATUS(status) != 0) && (_WSTATUS(status) != 0x7f)\n}\n\nprivate func WEXITSTATUS(_ status: Int32) -> Int32 {\n    (status >> 8) & 0xff\n}\n\nprivate func WTERMSIG(_ status: Int32) -> Int32 {\n    status & 0x7f\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/File.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// Trivial type to discover information about a given file (uid, gid, mode...).\npublic struct File: Sendable {\n    /// `File` errors.\n    public enum Error: Swift.Error, CustomStringConvertible {\n        case errno(_ e: Int32)\n\n        public var description: String {\n            switch self {\n            case .errno(let code):\n                return \"errno \\(code)\"\n            }\n        }\n    }\n\n    /// Returns a `FileInfo` struct with information about the file.\n    /// - Parameters:\n    ///   - url: The path to the file.\n    public static func info(_ url: URL) throws -> FileInfo {\n        try info(url.path)\n    }\n\n    /// Returns a `FileInfo` struct with information about the file.\n    /// - Parameters:\n    ///   - path: The path to the file as a string.\n    public static func info(_ path: String) throws -> FileInfo {\n        var st = stat()\n        guard lstat(path, &st) == 0 else {\n            throw Error.errno(errno)\n        }\n        return FileInfo(path, stat: st)\n    }\n}\n\n/// `FileInfo` holds and provides easy access to stat(2) data\n/// for a file.\npublic struct FileInfo: Sendable {\n    private let _stat_t: Foundation.stat\n    private let _path: String\n\n    init(_ path: String, stat: stat) {\n        self._path = path\n        self._stat_t = stat\n    }\n\n    /// mode_t for the file.\n    public var mode: mode_t {\n        self._stat_t.st_mode\n    }\n\n    /// The files uid.\n    public var uid: Int {\n        Int(self._stat_t.st_uid)\n    }\n\n    /// The files gid.\n    public var gid: Int {\n        Int(self._stat_t.st_gid)\n    }\n\n    /// The filesystem ID the file belongs to.\n    public var dev: Int {\n        Int(self._stat_t.st_dev)\n    }\n\n    /// The files inode number.\n    public var ino: Int {\n        Int(self._stat_t.st_ino)\n    }\n\n    /// The size of the file.\n    public var size: Int {\n        Int(self._stat_t.st_size)\n    }\n\n    /// The path to the file.\n    public var path: String {\n        self._path\n    }\n\n    /// Returns if the file is a directory.\n    public var isDirectory: Bool {\n        mode & S_IFMT == S_IFDIR\n    }\n\n    /// Returns if the file is a pipe.\n    public var isPipe: Bool {\n        mode & S_IFMT == S_IFIFO\n    }\n\n    /// Returns if the file is a socket.\n    public var isSocket: Bool {\n        mode & S_IFMT == S_IFSOCK\n    }\n\n    /// Returns if the file is a link.\n    public var isLink: Bool {\n        mode & S_IFMT == S_IFLNK\n    }\n\n    /// Returns if the file is a regular file.\n    public var isRegularFile: Bool {\n        mode & S_IFMT == S_IFREG\n    }\n\n    /// Returns if the file is a block device.\n    public var isBlock: Bool {\n        mode & S_IFMT == S_IFBLK\n    }\n\n    /// Returns if the file is a character device.\n    public var isChar: Bool {\n        mode & S_IFMT == S_IFCHR\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/FileDescriptor+SecurePath.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport SystemPackage\n\n#if canImport(Darwin)\nimport Darwin\nlet os_dup = Darwin.dup\n#elseif canImport(Musl)\nimport CSystem\nimport Musl\nlet os_dup = Musl.dup\n#elseif canImport(Glibc)\nimport Glibc\nlet os_dup = Glibc.dup\n#endif\n\nextension FileDescriptor {\n    /// Creates a directory relative to the FileDescriptor, rejecting\n    /// paths that traverse symlinks.\n    ///\n    /// - Parameters:\n    ///   - relativePath: The path to the directory to create, relative to the FileDescriptor\n    ///   - permissions: The permissions to give the directory (default is 0o755)\n    ///   - makeIntermediates: Create or replace intermediate components as needed\n    ///   - completion: A function that operates on the new directory\n    /// - Throws: `SecurePathError` if path validation or system errors occur\n    public func mkdirSecure(\n        _ relativePath: FilePath,\n        permissions: FilePermissions? = nil,\n        makeIntermediates: Bool = false,\n        completion: (FileDescriptor) throws -> Void = { _ in }\n    ) throws {\n        try Self.validateRelativePath(relativePath)\n        try mkdirSecure(\n            relativePath.components,\n            permissions: permissions,\n            makeIntermediates: makeIntermediates,\n            completion: completion\n        )\n    }\n\n    /// Recursively removes a direct child of a directory FileDescriptor.\n    ///\n    /// - Parameters:\n    ///   - filename: The name of the child file\n    /// - Throws: `SecurePathError` if system errors occur\n    public func unlinkRecursiveSecure(filename: FilePath.Component) throws {\n        guard filename.string != \".\" && filename.string != \"..\" else {\n            return\n        }\n\n        // Try to remove as a file, and continue if the remove fails.\n        guard unlinkat(self.rawValue, filename.string, 0) != 0 else {\n            return\n        }\n\n        // Return if the file already doesn't exist.\n        guard errno != ENOENT else {\n            return\n        }\n\n        // If the file is not a directory, then throw a real error.\n        guard errno == EPERM || errno == EISDIR else {\n            throw SecurePathError.systemError(\"file removal during secure unlink\", errno)\n        }\n\n        // Get the fd for the next path component.\n        let componentFd = openat(self.rawValue, filename.string, O_NOFOLLOW | O_RDONLY | O_DIRECTORY)\n        guard componentFd >= 0 else {\n            throw SecurePathError.systemError(\"directory open during secure unlink\", errno)\n        }\n        let componentFileDescriptor = FileDescriptor(rawValue: componentFd)\n        defer { try? componentFileDescriptor.close() }\n\n        // Open the directory stream using a duplicate fd that closedir() will close.\n        let ownedFd = os_dup(componentFd)\n        guard let dir = fdopendir(ownedFd) else {\n            throw SecurePathError.systemError(\"directory opendir during secure unlink\", errno)\n        }\n        defer { closedir(dir) }\n\n        // Recurse into each directory entry.\n        while let entry = readdir(dir) {\n            let childComponent = withUnsafePointer(to: entry.pointee.d_name) {\n                $0.withMemoryRebound(to: UInt8.self, capacity: Int(NAME_MAX) + 1) {\n                    let name = String(decodingCString: $0, as: UTF8.self)\n                    return FilePath.Component(name)\n                }\n            }\n            guard let childComponent else {\n                throw SecurePathError.systemError(\"directory entry processing during secure unlink\", errno)\n            }\n            try componentFileDescriptor.unlinkRecursiveSecure(filename: childComponent)\n        }\n\n        // The current directory is empty now, remove it.\n        if unlinkat(self.rawValue, filename.string, AT_REMOVEDIR) != 0 {\n            throw SecurePathError.systemError(\"directory removal during secure unlink\", errno)\n        }\n    }\n\n    private func mkdirSecure(\n        _ relativeComponents: FilePath.ComponentView,\n        permissions: FilePermissions? = nil,\n        makeIntermediates: Bool,\n        completion: (FileDescriptor) throws -> Void\n    ) throws {\n        // If the relative path is empty, call completion with self (the parent directory)\n        guard let currentComponent = relativeComponents.first else {\n            try completion(self)\n            return\n        }\n        let childComponents = FilePath.ComponentView(relativeComponents.dropFirst())\n\n        // Create or replace the directory as needed.\n        let parentFd = self.rawValue\n        var componentFd = openat(parentFd, currentComponent.string, O_NOFOLLOW | O_RDONLY | O_DIRECTORY)\n        if componentFd < 0 {\n            // If the non-directory component should be replaced with a directory, remove the component.\n            guard makeIntermediates || childComponents.isEmpty else {\n                throw SecurePathError.invalidPathComponent\n            }\n            if errno != ENOENT {\n                try self.unlinkRecursiveSecure(filename: currentComponent)\n            }\n\n            // Create and open an empty directory.\n            guard mkdirat(parentFd, currentComponent.string, permissions?.rawValue ?? 0o755) == 0 else {\n                throw SecurePathError.systemError(\"directory creation during secure mkdir\", errno)\n            }\n\n            componentFd = openat(parentFd, currentComponent.string, O_NOFOLLOW | O_RDONLY | O_DIRECTORY)\n            guard componentFd >= 0 else {\n                throw SecurePathError.systemError(\"directory open during secure mkdir\", errno)\n            }\n        }\n\n        let componentFileDescriptor = FileDescriptor(rawValue: componentFd)\n        defer { try? componentFileDescriptor.close() }\n\n        // Call the completion closure for the last component.\n        guard !childComponents.isEmpty else {\n            try completion(componentFileDescriptor)\n            return\n        }\n\n        // Create the directory for the remaining components.\n        try componentFileDescriptor.mkdirSecure(childComponents, permissions: permissions, makeIntermediates: makeIntermediates, completion: completion)\n    }\n\n    private static func validateRelativePath(_ path: FilePath) throws {\n        // Allow absolute paths; only the components will be used during traversal.\n        guard !(path.components.contains { $0 == \"..\" }) else {\n            throw SecurePathError.invalidRelativePath\n        }\n    }\n\n    #if canImport(Darwin)\n    public func getCanonicalPath() throws -> FilePath {\n        var buffer = [CChar](repeating: 0, count: Int(PATH_MAX))\n        guard fcntl(self.rawValue, F_GETPATH, &buffer) != -1 else {\n            throw Errno(rawValue: errno)\n        }\n\n        let bytes = buffer.prefix { $0 != 0 }.map { UInt8(bitPattern: $0) }\n        let pathname = String(decoding: bytes, as: UTF8.self)\n        return FilePath(pathname)\n    }\n    #elseif canImport(Glibc) || canImport(Musl)\n    public func getCanonicalPath() throws -> FilePath {\n        let fdPath = \"/proc/self/fd/\\(self.rawValue)\"\n        // Use readlink to resolve the symlink\n        var buffer = [CChar](repeating: 0, count: 4096)\n        let len = readlink(fdPath, &buffer, buffer.count - 1)\n        guard len > 0 else {\n            throw SecurePathError.systemError(\"readlink\", errno)\n        }\n        // Convert to bytes without null termination\n        let bytes = buffer.prefix(len).map { UInt8(bitPattern: $0) }\n        let pathname = String(decoding: bytes, as: UTF8.self)\n        return FilePath(pathname)\n    }\n    #endif\n}\n\npublic enum SecurePathError: Error, CustomStringConvertible, Equatable {\n    case invalidRelativePath\n    case invalidPathComponent\n    case cannotFollowSymlink\n    case systemError(String, Int32)\n\n    public var description: String {\n        switch self {\n        case .invalidRelativePath:\n            return \"invalid relative path supplied to secure path operation\"\n        case .invalidPathComponent:\n            return \"an intermediate path component is missing or is not a directory\"\n        case .cannotFollowSymlink:\n            return \"cannot follow a symlink an a secure path operation\"\n        case .systemError(let operation, let err):\n            return \"\\(operation) returned error: \\(err)\"\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/Keychain/KeychainQuery.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(macOS)\nimport Foundation\n\n/// Holds the result of a query to the keychain.\npublic struct KeychainQueryResult {\n    public var username: String\n    public var password: String\n    public var modifiedDate: Date\n    public var createdDate: Date\n}\n\n/// Type that facilitates interacting with the macOS keychain.\npublic struct KeychainQuery {\n    public init() {}\n\n    /// Save a value to the keychain.\n    /// - Parameters:\n    ///   - securityDomain: The security domain used to fetch keychain entries.\n    ///   - accessGroup: If present, the access group used to fetch keychain entries.\n    ///   - hostname: The hostname for the authenticating server.\n    ///   - username: The username to present to the server.\n    ///   - password: The password to present to the server.\n    /// - Throws: An error if the keychain query fails or returns unexpected data.\n    public func save(\n        securityDomain: String,\n        accessGroup: String? = nil,\n        hostname: String,\n        username: String,\n        password: String\n    ) throws {\n        if try exists(securityDomain: securityDomain, accessGroup: accessGroup, hostname: hostname) {\n            try delete(securityDomain: securityDomain, accessGroup: accessGroup, hostname: hostname)\n        }\n\n        guard let passwordEncoded = password.data(using: String.Encoding.utf8) else {\n            throw Self.Error.invalidPasswordConversion\n        }\n        var query: [String: Any] = [\n            kSecClass as String: kSecClassInternetPassword,\n            kSecAttrSecurityDomain as String: securityDomain,\n            kSecAttrServer as String: hostname,\n            kSecAttrAccount as String: username,\n            kSecValueData as String: passwordEncoded,\n            kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock,\n            kSecAttrSynchronizable as String: false,\n        ]\n        if let accessGroup {\n            query[kSecAttrAccessGroup as String] = accessGroup\n        }\n\n        let status = SecItemAdd(query as CFDictionary, nil)\n        guard status == errSecSuccess else { throw Self.Error.unhandledError(status: status) }\n    }\n\n    /// Delete a value from the keychain.\n    /// - Parameters:\n    ///   - securityDomain: The security domain used to fetch keychain entries.\n    ///   - accessGroup: If present, the access group used to fetch keychain entries.\n    ///   - hostname: The hostname for the authenticating server.\n    /// - Throws: An error if the keychain query fails or returns unexpected data.\n    public func delete(securityDomain: String, accessGroup: String? = nil, hostname: String) throws {\n        var query: [String: Any] = [\n            kSecClass as String: kSecClassInternetPassword,\n            kSecAttrSecurityDomain as String: securityDomain,\n            kSecAttrServer as String: hostname,\n            kSecMatchLimit as String: kSecMatchLimitOne,\n        ]\n        if let accessGroup {\n            query[kSecAttrAccessGroup as String] = accessGroup\n        }\n        let status = SecItemDelete(query as CFDictionary)\n        guard status == errSecSuccess || status == errSecItemNotFound else {\n            throw Self.Error.unhandledError(status: status)\n        }\n    }\n\n    /// Retrieve a value from the keychain.\n    /// - Parameters:\n    ///   - securityDomain: The security domain used to fetch keychain entries.\n    ///   - accessGroup: If present, the access group used to fetch keychain entries.\n    ///   - hostname: The hostname for the authenticating server.\n    /// - Returns: The keychain entry.\n    /// - Throws: An error if the keychain query fails or returns unexpected data.\n    public func get(securityDomain: String, accessGroup: String? = nil, hostname: String) throws -> KeychainQueryResult? {\n        var query: [String: Any] = [\n            kSecClass as String: kSecClassInternetPassword,\n            kSecAttrSecurityDomain as String: securityDomain,\n            kSecAttrServer as String: hostname,\n            kSecReturnAttributes as String: true,\n            kSecMatchLimit as String: kSecMatchLimitOne,\n            kSecReturnData as String: true,\n        ]\n        if let accessGroup {\n            query[kSecAttrAccessGroup as String] = accessGroup\n        }\n        var item: CFTypeRef?\n        let status = SecItemCopyMatching(query as CFDictionary, &item)\n        let exists = try isQuerySuccessful(status)\n        if !exists {\n            return nil\n        }\n\n        guard let fetched = item as? [String: Any] else {\n            throw Self.Error.unexpectedDataFetched\n        }\n        guard let data = fetched[kSecValueData as String] as? Data else {\n            throw Self.Error.keyNotPresent(key: kSecValueData as String)\n        }\n        guard let password = String(data: data, encoding: String.Encoding.utf8) else {\n            throw Self.Error.unexpectedDataFetched\n        }\n        guard let username = fetched[kSecAttrAccount as String] as? String else {\n            throw Self.Error.keyNotPresent(key: kSecAttrAccount as String)\n        }\n        guard let modifiedDate = fetched[kSecAttrModificationDate as String] as? Date else {\n            throw Self.Error.keyNotPresent(key: kSecAttrModificationDate as String)\n        }\n        guard let createdDate = fetched[kSecAttrCreationDate as String] as? Date else {\n            throw Self.Error.keyNotPresent(key: kSecAttrCreationDate as String)\n        }\n        return KeychainQueryResult(\n            username: username,\n            password: password,\n            modifiedDate: modifiedDate,\n            createdDate: createdDate\n        )\n    }\n\n    /// List all keychain entries for a domain.\n    /// - Parameters:\n    ///   - securityDomain: The security domain used to fetch keychain entries.\n    ///   - accessGroup: If present, the access group used to fetch keychain entries.\n    /// - Returns: An array of keychain metadata for each matching entry, or an empty array if none are found.\n    /// - Throws: An error if the keychain query fails or returns unexpected data.\n    public func list(securityDomain: String, accessGroup: String? = nil) throws -> [RegistryInfo] {\n        var query: [String: Any] = [\n            kSecClass as String: kSecClassInternetPassword,\n            kSecAttrSecurityDomain as String: securityDomain,\n            kSecReturnAttributes as String: true,\n            kSecReturnData as String: false,\n            kSecMatchLimit as String: kSecMatchLimitAll,\n        ]\n        if let accessGroup {\n            query[kSecAttrAccessGroup as String] = accessGroup\n        }\n        var item: CFTypeRef?\n        let status = SecItemCopyMatching(query as CFDictionary, &item)\n        let exists = try isQuerySuccessful(status)\n        if !exists {\n            return []\n        }\n\n        guard let fetched = item as? [[String: Any]] else {\n            throw Self.Error.unexpectedDataFetched\n        }\n\n        return try fetched.map { registry in\n            guard let hostname = registry[kSecAttrServer as String] as? String else {\n                throw Self.Error.keyNotPresent(key: kSecAttrServer as String)\n            }\n            guard let username = registry[kSecAttrAccount as String] as? String else {\n                throw Self.Error.keyNotPresent(key: kSecAttrAccount as String)\n            }\n            guard let modifiedDate = registry[kSecAttrModificationDate as String] as? Date else {\n                throw Self.Error.keyNotPresent(key: kSecAttrModificationDate as String)\n            }\n            guard let createdDate = registry[kSecAttrCreationDate as String] as? Date else {\n                throw Self.Error.keyNotPresent(key: kSecAttrCreationDate as String)\n            }\n\n            return RegistryInfo(\n                hostname: hostname,\n                username: username,\n                modifiedDate: modifiedDate,\n                createdDate: createdDate\n            )\n        }\n    }\n\n    /// Check if a value exists in the keychain.\n    /// - Parameters:\n    ///   - securityDomain: The security domain used to fetch keychain entries.\n    ///   - accessGroup: If present, the access group used to fetch keychain entries.\n    ///   - hostname: The hostname for the authenticating server.\n    /// - Returns: `true` if the entry exists, `false` otherwise.\n    /// - Throws: An error if the keychain query fails.\n    public func exists(securityDomain: String, accessGroup: String? = nil, hostname: String) throws -> Bool {\n        var query: [String: Any] = [\n            kSecClass as String: kSecClassInternetPassword,\n            kSecAttrSecurityDomain as String: securityDomain,\n            kSecAttrServer as String: hostname,\n            kSecReturnAttributes as String: true,\n            kSecMatchLimit as String: kSecMatchLimitOne,\n            kSecReturnData as String: false,\n        ]\n        if let accessGroup {\n            query[kSecAttrAccessGroup as String] = accessGroup\n        }\n\n        let status = SecItemCopyMatching(query as CFDictionary, nil)\n        return try isQuerySuccessful(status)\n    }\n\n    private func isQuerySuccessful(_ status: Int32) throws -> Bool {\n        guard status != errSecItemNotFound else {\n            return false\n        }\n        guard status == errSecSuccess else {\n            throw Self.Error.unhandledError(status: status)\n        }\n        return true\n    }\n}\n\nextension KeychainQuery {\n    public enum Error: Swift.Error {\n        case unhandledError(status: Int32)\n        case unexpectedDataFetched\n        case keyNotPresent(key: String)\n        case invalidPasswordConversion\n    }\n}\n#endif\n"
  },
  {
    "path": "Sources/ContainerizationOS/Keychain/RegistryInfo.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// Holds the stored attributes for a registry.\npublic struct RegistryInfo: Sendable {\n    /// The registry host as a domain name with an optional port.\n    public var hostname: String\n    /// The username used to authenticate with the registry.\n    public var username: String\n    /// The date the registry was last modified.\n    public let modifiedDate: Date\n    /// The date the registry was created.\n    public let createdDate: Date\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/Linux/Binfmt.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n#if canImport(Musl)\nimport Musl\nprivate let _mount = Musl.mount\n#elseif canImport(Glibc)\nimport Glibc\nprivate let _mount = Glibc.mount\n#endif\n\n/// `Binfmt` is a utility type that contains static helpers and types for\n/// mounting the Linux binfmt_misc filesystem, and creating new binfmt entries.\npublic struct Binfmt: Sendable {\n    /// Default mount path for binfmt_misc.\n    public static let path = \"/proc/sys/fs/binfmt_misc\"\n\n    /// Entry models a binfmt_misc entry.\n    /// https://docs.kernel.org/admin-guide/binfmt-misc.html\n    public struct Entry {\n        public var name: String\n        public var type: String\n        public var offset: String\n        public var magic: String\n        public var mask: String\n        public var flags: String\n\n        public init(\n            name: String,\n            type: String = \"M\",\n            offset: String = \"\",\n            magic: String,\n            mask: String,\n            flags: String = \"CF\"\n        ) {\n            self.name = name\n            self.type = type\n            self.offset = offset\n            self.magic = magic\n            self.mask = mask\n            self.flags = flags\n        }\n\n        /// Returns a binfmt `Entry` for amd64 ELF binaries.\n        public static func amd64() -> Self {\n            Binfmt.Entry(\n                name: \"x86_64\",\n                magic: #\"\\x7fELF\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x3e\\x00\"#,\n                mask: #\"\\xff\\xff\\xff\\xff\\xff\\xfe\\xfe\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff\"#\n            )\n        }\n\n        #if os(Linux)\n        /// Register the passed in `binaryPath` as the interpreter for a new binfmt_misc entry.\n        public func register(binaryPath: String) throws {\n            let registration = \":\\(self.name):\\(self.type):\\(self.offset):\\(self.magic):\\(self.mask):\\(binaryPath):\\(self.flags)\"\n\n            try registration.write(\n                to: URL(fileURLWithPath: Binfmt.path).appendingPathComponent(\"register\"),\n                atomically: false,\n                encoding: .ascii\n            )\n        }\n\n        /// Deregister the binfmt_misc entry described by the current object.\n        public func deregister() throws {\n            let data = \"-1\"\n            try data.write(\n                to: URL(fileURLWithPath: Binfmt.path).appendingPathComponent(self.name),\n                atomically: false,\n                encoding: .ascii\n            )\n        }\n        #endif  // os(Linux)\n    }\n\n    #if os(Linux)\n    /// Crude check to see if /proc/sys/fs/binfmt_misc/register exists.\n    public static func mounted() -> Bool {\n        FileManager.default.fileExists(atPath: \"\\(Self.path)/register\")\n    }\n\n    /// Mount the binfmt_misc filesystem.\n    public static func mount() throws {\n        guard _mount(\"binfmt_misc\", Self.path, \"binfmt_misc\", 0, \"\") == 0 else {\n            throw POSIXError.fromErrno()\n        }\n    }\n    #endif  // os(Linux)\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/Linux/Capabilities.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport CShim\nimport Foundation\n\n// MARK: - Configuration Types\n\npublic enum CapabilityParsingError: Swift.Error, CustomStringConvertible {\n    case invalidCapabilitySet(String)\n    case invalidCapabilityName(String)\n\n    public var description: String {\n        switch self {\n        case .invalidCapabilitySet(let value):\n            return \"invalid CapabilitySet value '\\(value)'\"\n        case .invalidCapabilityName(let value):\n            return \"invalid CapabilityName '\\(value)'\"\n        }\n    }\n}\n\npublic struct CapabilitySet: Sendable, Hashable {\n    private enum Value: Hashable, Sendable, CaseIterable {\n        case bounding\n        case effective\n        case inheritable\n        case permitted\n        case ambient\n    }\n\n    private var value: Value\n    private init(_ value: Value) {\n        self.value = value\n    }\n\n    public init(rawValue: String) throws {\n        let values = Value.allCases.reduce(into: [String: Value]()) {\n            $0[String(describing: $1).lowercased()] = $1\n        }\n\n        guard let match = values[rawValue.lowercased()] else {\n            throw CapabilityParsingError.invalidCapabilitySet(rawValue)\n        }\n        self.value = match\n    }\n\n    public static var bounding: Self { Self(.bounding) }\n    public static var effective: Self { Self(.effective) }\n    public static var inheritable: Self { Self(.inheritable) }\n    public static var permitted: Self { Self(.permitted) }\n    public static var ambient: Self { Self(.ambient) }\n}\n\nextension CapabilitySet: CustomStringConvertible {\n    public var description: String {\n        String(describing: self.value)\n    }\n}\n\npublic struct CapabilityName: Sendable, Hashable {\n    private enum Value: Hashable, Sendable, CaseIterable {\n        case chown\n        case dacOverride\n        case dacReadSearch\n        case fowner\n        case fsetid\n        case kill\n        case setgid\n        case setuid\n        case setpcap\n        case linuxImmutable\n        case netBindService\n        case netBroadcast\n        case netAdmin\n        case netRaw\n        case ipcLock\n        case ipcOwner\n        case sysModule\n        case sysRawio\n        case sysChroot\n        case sysPtrace\n        case sysPacct\n        case sysAdmin\n        case sysBoot\n        case sysNice\n        case sysResource\n        case sysTime\n        case sysTtyConfig\n        case mknod\n        case lease\n        case auditWrite\n        case auditControl\n        case setfcap\n        case macOverride\n        case macAdmin\n        case syslog\n        case wakeAlarm\n        case blockSuspend\n        case auditRead\n        case perfmon\n        case bpf\n        case checkpointRestore\n    }\n\n    private var value: Value\n    private init(_ value: Value) {\n        self.value = value\n    }\n\n    public init(rawValue: String) throws {\n        let uppercased = rawValue.uppercased()\n        let normalized = uppercased.hasPrefix(\"CAP_\") ? uppercased : \"CAP_\\(uppercased)\"\n\n        let capNameMap: [String: Value] = [\n            \"CAP_CHOWN\": .chown,\n            \"CAP_DAC_OVERRIDE\": .dacOverride,\n            \"CAP_DAC_READ_SEARCH\": .dacReadSearch,\n            \"CAP_FOWNER\": .fowner,\n            \"CAP_FSETID\": .fsetid,\n            \"CAP_KILL\": .kill,\n            \"CAP_SETGID\": .setgid,\n            \"CAP_SETUID\": .setuid,\n            \"CAP_SETPCAP\": .setpcap,\n            \"CAP_LINUX_IMMUTABLE\": .linuxImmutable,\n            \"CAP_NET_BIND_SERVICE\": .netBindService,\n            \"CAP_NET_BROADCAST\": .netBroadcast,\n            \"CAP_NET_ADMIN\": .netAdmin,\n            \"CAP_NET_RAW\": .netRaw,\n            \"CAP_IPC_LOCK\": .ipcLock,\n            \"CAP_IPC_OWNER\": .ipcOwner,\n            \"CAP_SYS_MODULE\": .sysModule,\n            \"CAP_SYS_RAWIO\": .sysRawio,\n            \"CAP_SYS_CHROOT\": .sysChroot,\n            \"CAP_SYS_PTRACE\": .sysPtrace,\n            \"CAP_SYS_PACCT\": .sysPacct,\n            \"CAP_SYS_ADMIN\": .sysAdmin,\n            \"CAP_SYS_BOOT\": .sysBoot,\n            \"CAP_SYS_NICE\": .sysNice,\n            \"CAP_SYS_RESOURCE\": .sysResource,\n            \"CAP_SYS_TIME\": .sysTime,\n            \"CAP_SYS_TTY_CONFIG\": .sysTtyConfig,\n            \"CAP_MKNOD\": .mknod,\n            \"CAP_LEASE\": .lease,\n            \"CAP_AUDIT_WRITE\": .auditWrite,\n            \"CAP_AUDIT_CONTROL\": .auditControl,\n            \"CAP_SETFCAP\": .setfcap,\n            \"CAP_MAC_OVERRIDE\": .macOverride,\n            \"CAP_MAC_ADMIN\": .macAdmin,\n            \"CAP_SYSLOG\": .syslog,\n            \"CAP_WAKE_ALARM\": .wakeAlarm,\n            \"CAP_BLOCK_SUSPEND\": .blockSuspend,\n            \"CAP_AUDIT_READ\": .auditRead,\n            \"CAP_PERFMON\": .perfmon,\n            \"CAP_BPF\": .bpf,\n            \"CAP_CHECKPOINT_RESTORE\": .checkpointRestore,\n        ]\n\n        guard let match = capNameMap[normalized] else {\n            throw CapabilityParsingError.invalidCapabilityName(rawValue)\n        }\n        self.value = match\n    }\n\n    public var capValue: UInt32 {\n        switch self.value {\n        case .chown: return 0\n        case .dacOverride: return 1\n        case .dacReadSearch: return 2\n        case .fowner: return 3\n        case .fsetid: return 4\n        case .kill: return 5\n        case .setgid: return 6\n        case .setuid: return 7\n        case .setpcap: return 8\n        case .linuxImmutable: return 9\n        case .netBindService: return 10\n        case .netBroadcast: return 11\n        case .netAdmin: return 12\n        case .netRaw: return 13\n        case .ipcLock: return 14\n        case .ipcOwner: return 15\n        case .sysModule: return 16\n        case .sysRawio: return 17\n        case .sysChroot: return 18\n        case .sysPtrace: return 19\n        case .sysPacct: return 20\n        case .sysAdmin: return 21\n        case .sysBoot: return 22\n        case .sysNice: return 23\n        case .sysResource: return 24\n        case .sysTime: return 25\n        case .sysTtyConfig: return 26\n        case .mknod: return 27\n        case .lease: return 28\n        case .auditWrite: return 29\n        case .auditControl: return 30\n        case .setfcap: return 31\n        case .macOverride: return 32\n        case .macAdmin: return 33\n        case .syslog: return 34\n        case .wakeAlarm: return 35\n        case .blockSuspend: return 36\n        case .auditRead: return 37\n        case .perfmon: return 38\n        case .bpf: return 39\n        case .checkpointRestore: return 40\n        }\n    }\n\n    public static var chown: Self { Self(.chown) }\n    public static var dacOverride: Self { Self(.dacOverride) }\n    public static var dacReadSearch: Self { Self(.dacReadSearch) }\n    public static var fowner: Self { Self(.fowner) }\n    public static var fsetid: Self { Self(.fsetid) }\n    public static var kill: Self { Self(.kill) }\n    public static var setgid: Self { Self(.setgid) }\n    public static var setuid: Self { Self(.setuid) }\n    public static var setpcap: Self { Self(.setpcap) }\n    public static var linuxImmutable: Self { Self(.linuxImmutable) }\n    public static var netBindService: Self { Self(.netBindService) }\n    public static var netBroadcast: Self { Self(.netBroadcast) }\n    public static var netAdmin: Self { Self(.netAdmin) }\n    public static var netRaw: Self { Self(.netRaw) }\n    public static var ipcLock: Self { Self(.ipcLock) }\n    public static var ipcOwner: Self { Self(.ipcOwner) }\n    public static var sysModule: Self { Self(.sysModule) }\n    public static var sysRawio: Self { Self(.sysRawio) }\n    public static var sysChroot: Self { Self(.sysChroot) }\n    public static var sysPtrace: Self { Self(.sysPtrace) }\n    public static var sysPacct: Self { Self(.sysPacct) }\n    public static var sysAdmin: Self { Self(.sysAdmin) }\n    public static var sysBoot: Self { Self(.sysBoot) }\n    public static var sysNice: Self { Self(.sysNice) }\n    public static var sysResource: Self { Self(.sysResource) }\n    public static var sysTime: Self { Self(.sysTime) }\n    public static var sysTtyConfig: Self { Self(.sysTtyConfig) }\n    public static var mknod: Self { Self(.mknod) }\n    public static var lease: Self { Self(.lease) }\n    public static var auditWrite: Self { Self(.auditWrite) }\n    public static var auditControl: Self { Self(.auditControl) }\n    public static var setfcap: Self { Self(.setfcap) }\n    public static var macOverride: Self { Self(.macOverride) }\n    public static var macAdmin: Self { Self(.macAdmin) }\n    public static var syslog: Self { Self(.syslog) }\n    public static var wakeAlarm: Self { Self(.wakeAlarm) }\n    public static var blockSuspend: Self { Self(.blockSuspend) }\n    public static var auditRead: Self { Self(.auditRead) }\n    public static var perfmon: Self { Self(.perfmon) }\n    public static var bpf: Self { Self(.bpf) }\n    public static var checkpointRestore: Self { Self(.checkpointRestore) }\n\n    public static var allCases: [CapabilityName] {\n        Value.allCases.map { CapabilityName($0) }\n    }\n}\n\nextension CapabilityName: CustomStringConvertible {\n    public var description: String {\n        switch self.value {\n        case .chown: return \"CAP_CHOWN\"\n        case .dacOverride: return \"CAP_DAC_OVERRIDE\"\n        case .dacReadSearch: return \"CAP_DAC_READ_SEARCH\"\n        case .fowner: return \"CAP_FOWNER\"\n        case .fsetid: return \"CAP_FSETID\"\n        case .kill: return \"CAP_KILL\"\n        case .setgid: return \"CAP_SETGID\"\n        case .setuid: return \"CAP_SETUID\"\n        case .setpcap: return \"CAP_SETPCAP\"\n        case .linuxImmutable: return \"CAP_LINUX_IMMUTABLE\"\n        case .netBindService: return \"CAP_NET_BIND_SERVICE\"\n        case .netBroadcast: return \"CAP_NET_BROADCAST\"\n        case .netAdmin: return \"CAP_NET_ADMIN\"\n        case .netRaw: return \"CAP_NET_RAW\"\n        case .ipcLock: return \"CAP_IPC_LOCK\"\n        case .ipcOwner: return \"CAP_IPC_OWNER\"\n        case .sysModule: return \"CAP_SYS_MODULE\"\n        case .sysRawio: return \"CAP_SYS_RAWIO\"\n        case .sysChroot: return \"CAP_SYS_CHROOT\"\n        case .sysPtrace: return \"CAP_SYS_PTRACE\"\n        case .sysPacct: return \"CAP_SYS_PACCT\"\n        case .sysAdmin: return \"CAP_SYS_ADMIN\"\n        case .sysBoot: return \"CAP_SYS_BOOT\"\n        case .sysNice: return \"CAP_SYS_NICE\"\n        case .sysResource: return \"CAP_SYS_RESOURCE\"\n        case .sysTime: return \"CAP_SYS_TIME\"\n        case .sysTtyConfig: return \"CAP_SYS_TTY_CONFIG\"\n        case .mknod: return \"CAP_MKNOD\"\n        case .lease: return \"CAP_LEASE\"\n        case .auditWrite: return \"CAP_AUDIT_WRITE\"\n        case .auditControl: return \"CAP_AUDIT_CONTROL\"\n        case .setfcap: return \"CAP_SETFCAP\"\n        case .macOverride: return \"CAP_MAC_OVERRIDE\"\n        case .macAdmin: return \"CAP_MAC_ADMIN\"\n        case .syslog: return \"CAP_SYSLOG\"\n        case .wakeAlarm: return \"CAP_WAKE_ALARM\"\n        case .blockSuspend: return \"CAP_BLOCK_SUSPEND\"\n        case .auditRead: return \"CAP_AUDIT_READ\"\n        case .perfmon: return \"CAP_PERFMON\"\n        case .bpf: return \"CAP_BPF\"\n        case .checkpointRestore: return \"CAP_CHECKPOINT_RESTORE\"\n        }\n    }\n}\n\n// MARK: - Linux Implementation\n\n#if os(Linux)\n\n#if canImport(Musl)\nimport Musl\n#elseif canImport(Glibc)\nimport Glibc\n#endif\n\nimport CShim\n\n/// Capability type flags\npublic struct CapType: OptionSet, Sendable {\n    public let rawValue: UInt32\n\n    public init(rawValue: UInt32) {\n        self.rawValue = rawValue\n    }\n\n    // Individual capability sets (for Get/Set/Unset/etc)\n    public static let effective = CapType(rawValue: 1 << 0)\n    public static let permitted = CapType(rawValue: 1 << 1)\n    public static let inheritable = CapType(rawValue: 1 << 2)\n    public static let bounding = CapType(rawValue: 1 << 3)\n    public static let ambient = CapType(rawValue: 1 << 4)\n\n    // Bulk operation flags (for Apply/Fill/Clear)\n    public static let caps = CapType(rawValue: 1 << 8)  // CAPS - effective, permitted, inheritable\n    public static let bounds = CapType(rawValue: 1 << 9)  // BOUNDS - bounding set\n    public static let ambs = CapType(rawValue: 1 << 10)  // AMBS - ambient capabilities\n}\n\nprivate struct CapabilityHeader {\n    var version: UInt32\n    var pid: Int32\n\n    init(pid: Int32 = 0) {\n        self.version = 0x2008_0522\n        self.pid = pid\n    }\n}\n\nprivate struct CapabilityData {\n    var effective1: UInt32\n    var permitted1: UInt32\n    var inheritable1: UInt32\n    var effective2: UInt32\n    var permitted2: UInt32\n    var inheritable2: UInt32\n\n    init(\n        effective1: UInt32 = 0,\n        permitted1: UInt32 = 0,\n        inheritable1: UInt32 = 0,\n        effective2: UInt32 = 0,\n        permitted2: UInt32 = 0,\n        inheritable2: UInt32 = 0\n    ) {\n        self.effective1 = effective1\n        self.permitted1 = permitted1\n        self.inheritable1 = inheritable1\n        self.effective2 = effective2\n        self.permitted2 = permitted2\n        self.inheritable2 = inheritable2\n    }\n}\n\n/// Interface with Linux capabilities\n/// https://linux.die.net/man/7/capabilities\npublic struct LinuxCapabilities: Sendable {\n    private var effectiveSet: UInt64 = 0\n    private var permittedSet: UInt64 = 0\n    private var inheritableSet: UInt64 = 0\n    private var boundingSet: UInt64 = 0\n    private var ambientSet: UInt64 = 0\n\n    public init() {}\n\n    /// Get the highest supported capability from the kernel\n    public static func getLastSupported() throws -> CapabilityName {\n        guard let data = try? String(contentsOfFile: \"/proc/sys/kernel/cap_last_cap\", encoding: .ascii),\n            let lastCap = UInt32(data.trimmingCharacters(in: .whitespacesAndNewlines))\n        else {\n            throw LinuxCapabilities.Error.invalidCapabilitySet(\"failed to read /proc/sys/kernel/cap_last_cap\")\n        }\n\n        guard let capability = CapabilityName.allCases.first(where: { $0.capValue == lastCap }) else {\n            throw LinuxCapabilities.Error.invalidCapabilitySet(\"no capability found for kernel max cap \\(lastCap)\")\n        }\n\n        return capability\n    }\n\n    /// Set keep caps\n    public static func setKeepCaps() throws {\n        let result = CZ_prctl_set_keepcaps()\n        if result != 0 {\n            throw LinuxCapabilities.Error.prctlFailed(errno: errno, operation: \"PR_SET_KEEPCAPS\")\n        }\n    }\n\n    /// Clear keep caps\n    public static func clearKeepCaps() throws {\n        let result = CZ_prctl_clear_keepcaps()\n        if result != 0 {\n            throw LinuxCapabilities.Error.prctlFailed(errno: errno, operation: \"PR_CLEAR_KEEPCAPS\")\n        }\n    }\n\n    /// Load current process capabilities from kernel\n    public mutating func load() throws {\n        let data = try getCurrentCapabilities()\n        self.effectiveSet = UInt64(data.effective1)\n        self.permittedSet = UInt64(data.permitted1)\n        self.inheritableSet = UInt64(data.inheritable1)\n    }\n\n    /// Check if capability is present in the given set\n    public func get(which: CapType, what: CapabilityName) -> Bool {\n        let bit = UInt64(1) << what.capValue\n\n        if which.contains(.effective) {\n            return (effectiveSet & bit) != 0\n        } else if which.contains(.permitted) {\n            return (permittedSet & bit) != 0\n        } else if which.contains(.inheritable) {\n            return (inheritableSet & bit) != 0\n        } else if which.contains(.bounding) {\n            return (boundingSet & bit) != 0\n        } else if which.contains(.ambient) {\n            return (ambientSet & bit) != 0\n        }\n        return false\n    }\n\n    /// Set capabilities in the given sets\n    public mutating func set(which: CapType, caps: [CapabilityName]) {\n        let mask = caps.reduce(UInt64(0)) { result, cap in\n            result | (UInt64(1) << cap.capValue)\n        }\n\n        if which.contains(.effective) {\n            effectiveSet |= mask\n        }\n        if which.contains(.permitted) {\n            permittedSet |= mask\n        }\n        if which.contains(.inheritable) {\n            inheritableSet |= mask\n        }\n        if which.contains(.bounding) {\n            boundingSet |= mask\n        }\n        if which.contains(.ambient) {\n            ambientSet |= mask\n        }\n    }\n\n    /// Unset capabilities from the given sets\n    public mutating func unset(which: CapType, caps: [CapabilityName]) {\n        let mask = caps.reduce(UInt64(0)) { result, cap in\n            result | (UInt64(1) << cap.capValue)\n        }\n\n        if which.contains(.effective) {\n            effectiveSet &= ~mask\n        }\n        if which.contains(.permitted) {\n            permittedSet &= ~mask\n        }\n        if which.contains(.inheritable) {\n            inheritableSet &= ~mask\n        }\n        if which.contains(.bounding) {\n            boundingSet &= ~mask\n        }\n        if which.contains(.ambient) {\n            ambientSet &= ~mask\n        }\n    }\n\n    /// Fill all bits of given capability types\n    public mutating func fill(kind: CapType) {\n        if kind.contains(.caps) {\n            effectiveSet = 0xFFFF_FFFF_FFFF_FFFF\n            permittedSet = 0xFFFF_FFFF_FFFF_FFFF\n            inheritableSet = 0\n        }\n        if kind.contains(.bounds) {\n            boundingSet = 0xFFFF_FFFF_FFFF_FFFF\n        }\n        if kind.contains(.ambs) {\n            ambientSet = 0xFFFF_FFFF_FFFF_FFFF\n        }\n    }\n\n    /// Clear all bits of given capability types\n    public mutating func clear(kind: CapType) {\n        if kind.contains(.caps) {\n            effectiveSet = 0\n            permittedSet = 0\n            inheritableSet = 0\n        }\n        if kind.contains(.bounds) {\n            boundingSet = 0\n        }\n        if kind.contains(.ambs) {\n            ambientSet = 0\n        }\n    }\n\n    /// Apply capabilities to current process\n    public func apply(kind: CapType) throws {\n        // Apply bounding set (requires CAP_SETPCAP)\n        if kind.contains(.bounds) {\n            try applyBoundingSet()\n        }\n\n        // Apply main capabilities (effective, permitted, inheritable)\n        if kind.contains(.caps) {\n            try applyMainCapabilities()\n        }\n\n        // Apply ambient capabilities\n        if kind.contains(.ambs) {\n            try applyAmbientCapabilities()\n        }\n    }\n\n    private func applyBoundingSet() throws {\n        let currentData = try getCurrentCapabilities()\n        let hasSetPCap = (currentData.effective1 & (1 << CapabilityName.setpcap.capValue)) != 0\n\n        if hasSetPCap {\n            // Get the last supported capability to avoid trying to drop unsupported ones\n            let lastSupported = try Self.getLastSupported()\n\n            for cap in CapabilityName.allCases {\n                // Skip capabilities higher than what the kernel supports\n                guard cap.capValue <= lastSupported.capValue else { continue }\n\n                let capBit = UInt64(1) << cap.capValue\n                if (boundingSet & capBit) == 0 {\n                    let result = CZ_prctl_capbset_drop(cap.capValue)\n                    if result != 0 && errno != EINVAL {\n                        throw Error.prctlFailed(errno: errno, operation: \"PR_CAPBSET_DROP\")\n                    }\n                }\n            }\n        }\n    }\n\n    private func applyMainCapabilities() throws {\n        let data = CapabilityData(\n            effective1: UInt32(effectiveSet & 0xFFFF_FFFF),\n            permitted1: UInt32(permittedSet & 0xFFFF_FFFF),\n            inheritable1: UInt32(inheritableSet & 0xFFFF_FFFF)\n        )\n\n        try setCapabilities(data: data)\n    }\n\n    private func applyAmbientCapabilities() throws {\n        // Clear all ambient capabilities first\n        let clearResult = CZ_prctl_cap_ambient_clear_all()\n        if clearResult != 0 && errno != EINVAL {\n            throw Error.prctlFailed(errno: errno, operation: \"PR_CAP_AMBIENT_CLEAR_ALL\")\n        }\n\n        // Get the last supported capability to avoid trying to set unsupported ones\n        let lastSupported = try Self.getLastSupported()\n\n        // Set each ambient capability\n        for cap in CapabilityName.allCases {\n            // Skip capabilities higher than what the kernel supports\n            guard cap.capValue <= lastSupported.capValue else { continue }\n\n            let capBit = UInt64(1) << cap.capValue\n            if (ambientSet & capBit) != 0 {\n                let result = CZ_prctl_cap_ambient_raise(cap.capValue)\n                if result != 0 && errno != EINVAL {\n                    throw Error.prctlFailed(errno: errno, operation: \"PR_CAP_AMBIENT_RAISE\")\n                }\n            }\n        }\n    }\n\n    private func getCurrentCapabilities() throws -> CapabilityData {\n        var header = CapabilityHeader()\n        var data = CapabilityData()\n\n        let result = withUnsafeMutablePointer(to: &header) { headerPtr in\n            withUnsafeMutablePointer(to: &data) { dataPtr in\n                CZ_capget(headerPtr, dataPtr)\n            }\n        }\n\n        if result != 0 {\n            throw Error.capgetFailed(errno: errno)\n        }\n\n        return data\n    }\n\n    private func setCapabilities(data: CapabilityData) throws {\n        var header = CapabilityHeader()\n        var mutableData = data\n\n        let result = withUnsafeMutablePointer(to: &header) { headerPtr in\n            withUnsafeMutablePointer(to: &mutableData) { dataPtr in\n                CZ_capset(headerPtr, dataPtr)\n            }\n        }\n\n        if result != 0 {\n            throw Error.capsetFailed(errno: errno)\n        }\n    }\n}\n\nextension LinuxCapabilities {\n    public enum Error: Swift.Error, CustomStringConvertible {\n        case unsupportedCapability(name: String)\n        case capsetFailed(errno: Int32)\n        case capgetFailed(errno: Int32)\n        case prctlFailed(errno: Int32, operation: String)\n        case invalidCapabilitySet(String)\n\n        public var description: String {\n            switch self {\n            case .unsupportedCapability(let name):\n                return \"unsupported capability: \\(name)\"\n            case .capsetFailed(let errno):\n                return \"capset failed with errno \\(errno): \\(String(cString: strerror(errno)))\"\n            case .capgetFailed(let errno):\n                return \"capget failed with errno \\(errno): \\(String(cString: strerror(errno)))\"\n            case .prctlFailed(let errno, let operation):\n                return \"prctl(\\(operation)) failed with errno \\(errno): \\(String(cString: strerror(errno)))\"\n            case .invalidCapabilitySet(let message):\n                return \"invalid capability set configuration: \\(message)\"\n            }\n        }\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/ContainerizationOS/Linux/Epoll.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if canImport(Musl)\nimport Musl\n\nimport Foundation\nimport Synchronization\n\n/// Register file descriptors to receive events via Linux's\n/// epoll syscall surface.\npublic final class Epoll: Sendable {\n    public typealias Mask = Int32\n    public typealias Handler = (@Sendable (Mask) -> Void)\n\n    private let epollFD: Int32\n    private let handlers = SafeMap<Int32, Handler>()\n    private let pipe = Pipe()  // to wake up a waiting epoll_wait\n\n    public init() throws {\n        let efd = epoll_create1(EPOLL_CLOEXEC)\n        guard efd > 0 else {\n            throw POSIXError.fromErrno()\n        }\n        self.epollFD = efd\n        try self.add(pipe.fileHandleForReading.fileDescriptor) { _ in }\n    }\n\n    public func add(\n        _ fd: Int32,\n        mask: Int32 = EPOLLIN | EPOLLOUT,  // HUP is always added\n        handler: @escaping Handler\n    ) throws {\n        guard fcntl(fd, F_SETFL, O_NONBLOCK) == 0 else {\n            throw POSIXError.fromErrno()\n        }\n\n        let events = EPOLLET | UInt32(bitPattern: mask)\n\n        var event = epoll_event()\n        event.events = events\n        event.data.fd = fd\n\n        try withUnsafeMutablePointer(to: &event) { ptr in\n            while true {\n                if epoll_ctl(self.epollFD, EPOLL_CTL_ADD, fd, ptr) == -1 {\n                    if errno == EAGAIN || errno == EINTR {\n                        continue\n                    }\n                    throw POSIXError.fromErrno()\n                }\n                break\n            }\n        }\n\n        self.handlers.set(fd, handler)\n    }\n\n    /// Run the main epoll loop.\n    ///\n    /// max events to return in a single wait\n    /// timeout in ms.\n    /// -1 means block forever.\n    /// 0 means return immediately if no events.\n    public func run(maxEvents: Int = 128, timeout: Int32 = -1) throws {\n        var events: [epoll_event] = .init(\n            repeating: epoll_event(),\n            count: maxEvents\n        )\n\n        while true {\n            let n = epoll_wait(self.epollFD, &events, Int32(events.count), timeout)\n            guard n >= 0 else {\n                if errno == EINTR || errno == EAGAIN {\n                    continue  // go back to epoll_wait\n                }\n                throw POSIXError.fromErrno()\n            }\n\n            if n == 0 {\n                return  // if epoll wait times out, then n will be 0\n            }\n\n            for i in 0..<Int(n) {\n                let fd = events[i].data.fd\n                let mask = events[i].events\n\n                if fd == self.pipe.fileHandleForReading.fileDescriptor {\n                    close(self.epollFD)\n                    return  // this is a shutdown message\n                }\n\n                guard let handler = handlers.get(fd) else {\n                    continue\n                }\n                handler(Int32(bitPattern: mask))\n            }\n        }\n    }\n\n    /// Remove the provided fd from the monitored collection.\n    public func delete(_ fd: Int32) throws {\n        var event = epoll_event()\n        let result = withUnsafeMutablePointer(to: &event) { ptr in\n            epoll_ctl(self.epollFD, EPOLL_CTL_DEL, fd, ptr)\n        }\n        if result != 0 {\n            if !acceptableDeletionErrno() {\n                throw POSIXError.fromErrno()\n            }\n        }\n        self.handlers.del(fd)\n    }\n\n    // The errno's here are acceptable and can happen if the caller\n    // closed the underlying fd before calling delete().\n    private func acceptableDeletionErrno() -> Bool {\n        errno == ENOENT || errno == EBADF || errno == EPERM\n    }\n\n    /// Shutdown the epoll handler.\n    public func shutdown() throws {\n        // wakes up epoll_wait and triggers a shutdown\n        try self.pipe.fileHandleForWriting.close()\n    }\n\n    private final class SafeMap<Key: Hashable & Sendable, Value: Sendable>: Sendable {\n        let dict = Mutex<[Key: Value]>([:])\n\n        func set(_ key: Key, _ value: Value) {\n            dict.withLock { @Sendable in\n                $0[key] = value\n            }\n        }\n\n        func get(_ key: Key) -> Value? {\n            dict.withLock { @Sendable in\n                $0[key]\n            }\n        }\n\n        func del(_ key: Key) {\n            dict.withLock { @Sendable in\n                _ = $0.removeValue(forKey: key)\n            }\n        }\n    }\n}\n\nextension Epoll.Mask {\n    public var isHangup: Bool {\n        (self & (EPOLLHUP | EPOLLERR)) != 0\n    }\n\n    public var isRhangup: Bool {\n        (self & EPOLLRDHUP) != 0\n    }\n\n    public var readyToRead: Bool {\n        (self & EPOLLIN) != 0\n    }\n\n    public var readyToWrite: Bool {\n        (self & EPOLLOUT) != 0\n    }\n}\n\n#endif  // canImport(Musl)\n"
  },
  {
    "path": "Sources/ContainerizationOS/Mount/Mount.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport CShim\nimport Foundation\n\n#if canImport(Musl)\nimport Musl\nprivate let _mount = Musl.mount\nprivate let _umount = Musl.umount2\n#elseif canImport(Glibc)\nimport Glibc\nprivate let _mount = Glibc.mount\nprivate let _umount = Glibc.umount2\n#endif\n\n// Mount package modeled closely from containerd's: https://github.com/containerd/containerd/tree/main/core/mount\n\n/// `Mount` models a Linux mount (although potentially could be used on other unix platforms), and\n/// provides a simple interface to mount what the type describes.\npublic struct Mount: Sendable {\n    // Type specifies the host-specific of the mount.\n    public var type: String\n    // Source specifies where to mount from. Depending on the host system, this\n    // can be a source path or device.\n    public var source: String\n    // Target specifies an optional subdirectory as a mountpoint.\n    public var target: String\n    // Options contains zero or more fstab-style mount options.\n    public var options: [String]\n\n    public init(type: String, source: String, target: String, options: [String]) {\n        self.type = type\n        self.source = source\n        self.target = target\n        self.options = options\n    }\n}\n\nextension Mount {\n    #if canImport(Glibc)\n    internal typealias Flag = Int\n    #else\n    internal typealias Flag = Int32\n    #endif\n\n    internal struct FlagBehavior {\n        let clear: Bool\n        let flag: Flag\n\n        public init(_ clear: Bool, _ flag: Flag) {\n            self.clear = clear\n            self.flag = flag\n        }\n    }\n\n    #if os(Linux)\n    internal static let flagsDictionary: [String: FlagBehavior] = [\n        \"async\": .init(true, MS_SYNCHRONOUS),\n        \"atime\": .init(true, MS_NOATIME),\n        \"bind\": .init(false, MS_BIND),\n        \"defaults\": .init(false, 0),\n        \"dev\": .init(true, MS_NODEV),\n        \"diratime\": .init(true, MS_NODIRATIME),\n        \"dirsync\": .init(false, MS_DIRSYNC),\n        \"exec\": .init(true, MS_NOEXEC),\n        \"mand\": .init(false, MS_MANDLOCK),\n        \"noatime\": .init(false, MS_NOATIME),\n        \"nodev\": .init(false, MS_NODEV),\n        \"nodiratime\": .init(false, MS_NODIRATIME),\n        \"noexec\": .init(false, MS_NOEXEC),\n        \"nomand\": .init(true, MS_MANDLOCK),\n        \"norelatime\": .init(true, MS_RELATIME),\n        \"nostrictatime\": .init(true, MS_STRICTATIME),\n        \"nosuid\": .init(false, MS_NOSUID),\n        \"rbind\": .init(false, MS_BIND | MS_REC),\n        \"relatime\": .init(false, MS_RELATIME),\n        \"remount\": .init(false, MS_REMOUNT),\n        \"ro\": .init(false, MS_RDONLY),\n        \"rw\": .init(true, MS_RDONLY),\n        \"strictatime\": .init(false, MS_STRICTATIME),\n        \"suid\": .init(true, MS_NOSUID),\n        \"sync\": .init(false, MS_SYNCHRONOUS),\n    ]\n\n    internal struct MountOptions {\n        var flags: Int32\n        var data: [String]\n\n        public init(_ flags: Int32 = 0, data: [String] = []) {\n            self.flags = flags\n            self.data = data\n        }\n    }\n\n    /// Whether the mount is read only.\n    public var readOnly: Bool {\n        for option in self.options {\n            if option == \"ro\" {\n                return true\n            }\n        }\n        return false\n    }\n\n    /// Mount the mount relative to `root` with the current set of data in the object.\n    ///\n    /// Optionally provide `createWithPerms` to set the permissions for the directory that\n    /// it will be mounted at.\n    public func mount(root: String, createWithPerms: Int16? = nil) throws {\n        let fd = try secureResolveInRoot(root: root)\n        defer { close(fd) }\n\n        let realPath = try readlinkProc(fd: fd)\n        try self.mountToTarget(target: realPath, createWithPerms: createWithPerms, targetResolved: true)\n    }\n\n    /// Open a path relative to `dirFd` using `openat2(2)` with `RESOLVE_IN_ROOT`.\n    ///\n    /// All symlink resolution is confined to the directory tree beneath `dirFd`.\n    /// Returns the file descriptor on success, or -1 on failure (with errno set).\n    private func openInRoot(dirFd: Int32, path: String, flags: Int32, mode: UInt64 = 0) -> Int32 {\n        path.withCString { cPath in\n            var how = cz_open_how(\n                flags: UInt64(flags),\n                mode: mode,\n                resolve: UInt64(RESOLVE_IN_ROOT)\n            )\n            return CZ_openat2(dirFd, cPath, &how, MemoryLayout<cz_open_how>.size)\n        }\n    }\n\n    private func secureResolveInRoot(root: String) throws -> Int32 {\n        let rootFd = open(root, O_RDONLY | O_DIRECTORY | O_CLOEXEC)\n        guard rootFd >= 0 else {\n            throw Error.errno(errno, \"failed to open rootfs '\\(root)'\")\n        }\n\n        // Determine if the leaf mount point should be a file or directory.\n        let opts = parseMountOptions()\n        let isBindMount = (opts.flags & Int32(MS_BIND)) != 0\n        var leafIsFile = false\n        if isBindMount {\n            var sourceStat = stat()\n            if stat(self.source, &sourceStat) == 0 {\n                leafIsFile = (sourceStat.st_mode & S_IFMT) != S_IFDIR\n            }\n        }\n\n        // Normalize target to a relative path for openat2.\n        let relativePath = self.target\n            .split(separator: \"/\", omittingEmptySubsequences: true)\n            .joined(separator: \"/\")\n\n        guard !relativePath.isEmpty else {\n            return rootFd\n        }\n\n        // Fast path: try openat2 with RESOLVE_IN_ROOT for the full path.\n        let openFlags: Int32 =\n            leafIsFile\n            ? (O_RDONLY | O_CLOEXEC)\n            : (O_RDONLY | O_DIRECTORY | O_CLOEXEC)\n        let fd = openInRoot(dirFd: rootFd, path: relativePath, flags: openFlags)\n        if fd >= 0 {\n            close(rootFd)\n            return fd\n        }\n\n        guard errno == ENOENT else {\n            let savedErrno = errno\n            close(rootFd)\n            throw Error.errno(savedErrno, \"failed to resolve '\\(self.target)' in rootfs\")\n        }\n\n        // Part of the path doesn't exist. Use openat2 to find the deepest\n        // existing ancestor, then create the missing components.\n        return try createMountTarget(\n            rootFd: rootFd, relativePath: relativePath, leafIsFile: leafIsFile\n        )\n    }\n\n    private func createMountTarget(\n        rootFd: Int32,\n        relativePath: String,\n        leafIsFile: Bool\n    ) throws -> Int32 {\n        let components = relativePath.split(separator: \"/\").map(String.init)\n        var currentFd = rootFd\n        var resultFd: Int32 = -1\n\n        // Centralized cleanup. On success resultFd holds the fd we return,\n        // so we avoid closing it. On error resultFd is -1 and we close\n        // everything.\n        defer {\n            if currentFd != rootFd && currentFd != resultFd { close(currentFd) }\n            if rootFd != resultFd { close(rootFd) }\n        }\n\n        func fail(_ savedErrno: Int32, _ message: String) throws -> Never {\n            throw Error.errno(savedErrno, message)\n        }\n\n        // Find the deepest existing directory using openat2 with RESOLVE_IN_ROOT.\n        var firstMissing = 0\n        for i in 0..<components.count {\n            let subpath = components[0...i].joined(separator: \"/\")\n            let nextFd = openInRoot(dirFd: rootFd, path: subpath, flags: O_RDONLY | O_DIRECTORY | O_CLOEXEC)\n            if nextFd < 0 {\n                firstMissing = i\n                break\n            }\n            if currentFd != rootFd { close(currentFd) }\n            currentFd = nextFd\n            firstMissing = i + 1\n        }\n\n        // Create missing directories and the leaf mount point.\n        for i in firstMissing..<components.count {\n            let component = components[i]\n            let isLast = (i == components.count - 1)\n\n            if isLast && leafIsFile {\n                // Use mknodat to create a regular file without opening it.\n                let rc = mknodat(currentFd, component, S_IFREG | 0o644, 0)\n                if rc != 0 && errno != EEXIST {\n                    try fail(errno, \"failed to create mount point file '\\(component)'\")\n                }\n                let pathFd = openat(currentFd, component, O_RDONLY | O_NOFOLLOW | O_CLOEXEC)\n                guard pathFd >= 0 else {\n                    try fail(errno, \"failed to re-open mount point file '\\(component)'\")\n                }\n                resultFd = pathFd\n                return resultFd\n            }\n\n            guard mkdirat(currentFd, component, 0o755) == 0 else {\n                try fail(errno, \"failed to create directory '\\(component)'\")\n            }\n\n            let dirFd = openat(currentFd, component, O_RDONLY | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC)\n            guard dirFd >= 0 else {\n                try fail(errno, \"failed to open created directory '\\(component)'\")\n            }\n\n            if isLast {\n                resultFd = dirFd\n                return resultFd\n            }\n\n            if currentFd != rootFd { close(currentFd) }\n            currentFd = dirFd\n        }\n\n        // All components already existed.\n        resultFd = currentFd\n        return resultFd\n    }\n\n    /// Resolve the real filesystem path for an open fd via /proc/self/fd.\n    private func readlinkProc(fd: Int32) throws -> String {\n        let procPath = \"/proc/self/fd/\\(fd)\"\n        var buffer = [CChar](repeating: 0, count: Int(PATH_MAX) + 1)\n        let len = readlink(procPath, &buffer, buffer.count - 1)\n        guard len > 0 else {\n            throw Error.errno(errno, \"readlink failed for '\\(procPath)'\")\n        }\n        return buffer.prefix(len).withUnsafeBufferPointer { buf in\n            String(decoding: buf.map { UInt8(bitPattern: $0) }, as: UTF8.self)\n        }\n    }\n\n    /// Mount the mount with the current set of data in the object. Optionally\n    /// provide `createWithPerms` to set the permissions for the directory that\n    /// it will be mounted at.\n    public func mount(createWithPerms: Int16? = nil) throws {\n        try self.mountToTarget(target: self.target, createWithPerms: createWithPerms)\n    }\n\n    private func mountToTarget(target: String, createWithPerms: Int16?, targetResolved: Bool = false) throws {\n        let pageSize = sysconf(Int32(_SC_PAGESIZE))\n\n        let opts = parseMountOptions()\n        let dataString = opts.data.joined(separator: \",\")\n        if dataString.count > pageSize {\n            throw Error.validation(\"data string exceeds page size (\\(dataString.count) > \\(pageSize))\")\n        }\n\n        let propagationTypes: Int32 = Int32(MS_SHARED) | Int32(MS_PRIVATE) | Int32(MS_SLAVE) | Int32(MS_UNBINDABLE)\n\n        // Ensure propagation type change flags aren't included in other calls.\n        let originalFlags = opts.flags & ~(propagationTypes)\n\n        // When targetResolved is true, the target path has already been securely\n        // resolved and the mount point created by secureResolveInRoot. Skip\n        // directory/file creation to avoid following symlinks in the target path.\n        if !targetResolved {\n            let targetURL = URL(fileURLWithPath: target)\n            let targetParent = targetURL.deletingLastPathComponent().path\n            if let perms = createWithPerms {\n                try mkdirAll(targetParent, perms)\n            }\n\n            // For bind mounts, check if the source is a file and create the target accordingly.\n            let isBindMount = (originalFlags & Int32(MS_BIND)) != 0\n            if isBindMount {\n                var sourceIsNonDir = false\n                var sourceStat = stat()\n                if stat(self.source, &sourceStat) == 0 {\n                    sourceIsNonDir = (sourceStat.st_mode & S_IFMT) != S_IFDIR\n                }\n\n                if sourceIsNonDir {\n                    // Create parent directories and touch the target file\n                    try mkdirAll(targetParent, 0o755)\n                    let fd = open(target, O_WRONLY | O_CREAT, 0o644)\n                    if fd >= 0 {\n                        close(fd)\n                    }\n                } else {\n                    try mkdirAll(target, 0o755)\n                }\n            } else {\n                try mkdirAll(target, 0o755)\n            }\n        }\n\n        if opts.flags & Int32(MS_REMOUNT) == 0 || !dataString.isEmpty {\n            guard _mount(self.source, target, self.type, UInt(originalFlags), dataString) == 0 else {\n                throw Error.errno(\n                    errno,\n                    \"failed initial mount source=\\(self.source) target=\\(target) type=\\(self.type) data=\\(dataString)\"\n                )\n            }\n        }\n\n        if opts.flags & propagationTypes != 0 {\n            // Change the propagation type.\n            let pflags = propagationTypes | Int32(MS_REC) | Int32(MS_SILENT)\n            guard _mount(\"\", target, \"\", UInt(opts.flags & pflags), \"\") == 0 else {\n                throw Error.errno(errno, \"failed propagation change mount\")\n            }\n        }\n\n        let bindReadOnlyFlags = Int32(MS_BIND) | Int32(MS_RDONLY)\n        if originalFlags & bindReadOnlyFlags == bindReadOnlyFlags {\n            guard _mount(\"\", target, \"\", UInt(originalFlags | Int32(MS_REMOUNT)), \"\") == 0 else {\n                throw Error.errno(errno, \"failed bind mount\")\n            }\n        }\n    }\n\n    private func mkdirAll(_ name: String, _ perm: Int16) throws {\n        try FileManager.default.createDirectory(\n            atPath: name,\n            withIntermediateDirectories: true,\n            attributes: [.posixPermissions: perm]\n        )\n    }\n\n    private func parseMountOptions() -> MountOptions {\n        var mountOpts = MountOptions()\n        for option in self.options {\n            if let entry = Self.flagsDictionary[option], entry.flag != 0 {\n                if entry.clear {\n                    mountOpts.flags &= ~Int32(entry.flag)\n                } else {\n                    mountOpts.flags |= Int32(entry.flag)\n                }\n            } else {\n                mountOpts.data.append(option)\n            }\n        }\n        return mountOpts\n    }\n\n    /// `Mount` errors\n    public enum Error: Swift.Error, CustomStringConvertible {\n        case errno(Int32, String)\n        case validation(String)\n\n        public var description: String {\n            switch self {\n            case .errno(let errno, let message):\n                return \"mount failed with errno \\(errno): \\(message)\"\n            case .validation(let message):\n                return \"failed during validation: \\(message)\"\n            }\n        }\n    }\n    #endif\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/POSIXError+Helpers.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\nextension POSIXError {\n    public static func fromErrno() -> POSIXError {\n        guard let errCode = POSIXErrorCode(rawValue: errno) else {\n            fatalError(\"failed to convert errno to POSIXErrorCode\")\n        }\n        return POSIXError(errCode)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/Path.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// `Path` provides utilities to look for binaries in the current PATH,\n/// or to return the current PATH.\npublic struct Path {\n    /// lookPath looks up an executable's path from $PATH\n    public static func lookPath(_ name: String) -> URL? {\n        lookup(name, path: getCurrentPath())\n    }\n\n    public static func lookPath(_ name: String, path: String) -> URL? {\n        lookup(name, path: path)\n    }\n\n    // getEnv returns the default environment of the process\n    // with the default $PATH added for the context of a macOS application bundle\n    public static func getEnv() -> [String: String] {\n        var env = ProcessInfo.processInfo.environment\n        env[\"PATH\"] = getCurrentPath()\n        return env\n    }\n\n    private static func lookup(_ name: String, path: String) -> URL? {\n        // Return nil for empty names\n        if name.isEmpty {\n            return nil\n        }\n\n        if name.contains(\"/\") {\n            if findExec(name) {\n                return URL(fileURLWithPath: name)\n            }\n            return nil\n        }\n\n        for var lookdir in path.split(separator: \":\") {\n            if lookdir.isEmpty {\n                lookdir = \".\"\n            }\n            let file = URL(fileURLWithPath: String(lookdir)).appendingPathComponent(name)\n            if findExec(file.path) {\n                return file\n            }\n        }\n        return nil\n    }\n\n    /// getPath returns $PATH for the current process\n    public static func getCurrentPath() -> String {\n        let env = ProcessInfo.processInfo.environment\n        return env[\"PATH\"] ?? \"/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin\"\n    }\n\n    // findPath returns a string containing the 'PATH' environment variable\n    public static func findPath(_ env: [String]?) -> String? {\n        guard let env = env else {\n            return nil\n        }\n        return env.first(where: { $0.hasPrefix(\"PATH=\") })\n            .map { String($0.dropFirst(5)) }\n    }\n\n    // findExec returns true if the provided path is an executable\n    private static func findExec(_ path: String) -> Bool {\n        let fm = FileManager.default\n        return fm.isExecutableFile(atPath: path)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/Pipe+Close.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\nextension Pipe {\n    /// Close both sides of the pipe.\n    public func close() throws {\n        var err: Swift.Error?\n        do {\n            try self.fileHandleForReading.close()\n        } catch {\n            err = error\n        }\n        try self.fileHandleForWriting.close()\n        if let err {\n            throw err\n        }\n    }\n\n    /// Ensure that both sides of the pipe are set with O_CLOEXEC.\n    public func setCloexec() throws {\n        if fcntl(self.fileHandleForWriting.fileDescriptor, F_SETFD, FD_CLOEXEC) == -1 {\n            throw POSIXError(.init(rawValue: errno)!)\n        }\n        if fcntl(self.fileHandleForReading.fileDescriptor, F_SETFD, FD_CLOEXEC) == -1 {\n            throw POSIXError(.init(rawValue: errno)!)\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/README.md",
    "content": "## OS\n\nThis target contains general useful OS related definitions or wrappers."
  },
  {
    "path": "Sources/ContainerizationOS/Reaper.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// A process reaper that returns exited processes along\n/// with their exit status.\npublic struct Reaper {\n    /// Process's pid and exit status.\n    typealias Exit = (pid: Int32, status: Int32)\n\n    /// Reap all pending processes and return the pid and exit status.\n    public static func reap() -> [Int32: Int32] {\n        var reaped = [Int32: Int32]()\n        while true {\n            guard let exit = wait() else {\n                return reaped\n            }\n            reaped[exit.pid] = exit.status\n        }\n        return reaped\n    }\n\n    /// Returns the exit status of the last process that exited.\n    /// nil is returned when no pending processes exist.\n    private static func wait() -> Exit? {\n        var rus = rusage()\n        var ws = Int32()\n\n        let pid = wait4(-1, &ws, WNOHANG, &rus)\n        if pid <= 0 {\n            return nil\n        }\n        return (pid: pid, status: Command.toExitStatus(ws))\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/Signals.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// Helper type with utilities to parse and manipulate unix signals.\npublic struct Signals {\n    /// Returns the numeric values of all known signals.\n    public static func allNumeric() -> [Int32] {\n        Array(Signals.all.values)\n    }\n\n    /// Parses a string representation of a signal (SIGKILL) and returns\n    // the 32 bit integer representation (9).\n    public static func parseSignal(_ signal: String) throws -> Int32 {\n        if let sig = Int32(signal) {\n            if !Signals.all.values.contains(sig) {\n                throw Error.invalidSignal(signal)\n            }\n            return sig\n        }\n        var signalUpper = signal.uppercased()\n        signalUpper.trimPrefix(\"SIG\")\n        guard let sig = Signals.all[signalUpper] else {\n            throw Error.invalidSignal(signal)\n        }\n        return sig\n    }\n\n    /// Errors that can be encountered for converting signals.\n    public enum Error: Swift.Error, CustomStringConvertible {\n        case invalidSignal(String)\n\n        public var description: String {\n            switch self {\n            case .invalidSignal(let sig):\n                return \"invalid signal: \\(sig)\"\n            }\n        }\n    }\n}\n\n#if os(macOS)\n\nextension Signals {\n    /// `all` returns all signals for the current platform.\n    public static let all: [String: Int32] = [\n        \"ABRT\": SIGABRT,\n        \"ALRM\": SIGALRM,\n        \"BUS\": SIGBUS,\n        \"CHLD\": SIGCHLD,\n        \"CONT\": SIGCONT,\n        \"EMT\": SIGEMT,\n        \"FPE\": SIGFPE,\n        \"HUP\": SIGHUP,\n        \"ILL\": SIGILL,\n        \"INFO\": SIGINFO,\n        \"INT\": SIGINT,\n        \"IO\": SIGIO,\n        \"IOT\": SIGIOT,\n        \"KILL\": SIGKILL,\n        \"PIPE\": SIGPIPE,\n        \"PROF\": SIGPROF,\n        \"QUIT\": SIGQUIT,\n        \"SEGV\": SIGSEGV,\n        \"STOP\": SIGSTOP,\n        \"SYS\": SIGSYS,\n        \"TERM\": SIGTERM,\n        \"TRAP\": SIGTRAP,\n        \"TSTP\": SIGTSTP,\n        \"TTIN\": SIGTTIN,\n        \"TTOU\": SIGTTOU,\n        \"URG\": SIGURG,\n        \"USR1\": SIGUSR1,\n        \"USR2\": SIGUSR2,\n        \"VTALRM\": SIGVTALRM,\n        \"WINCH\": SIGWINCH,\n        \"XCPU\": SIGXCPU,\n        \"XFSZ\": SIGXFSZ,\n    ]\n}\n\n#endif\n\n#if os(Linux)\n\nextension Signals {\n    /// `all` returns all signals for the current platform.\n    ///\n    /// For Linux this isn't actually exhaustive as it excludes\n    /// rtmin/rtmax entries.\n    public static let all: [String: Int32] = [\n        \"ABRT\": SIGABRT,\n        \"ALRM\": SIGALRM,\n        \"BUS\": SIGBUS,\n        \"CHLD\": SIGCHLD,\n        \"CLD\": SIGCHLD,\n        \"CONT\": SIGCONT,\n        \"FPE\": SIGFPE,\n        \"HUP\": SIGHUP,\n        \"ILL\": SIGILL,\n        \"INT\": SIGINT,\n        \"IO\": SIGIO,\n        \"IOT\": SIGIOT,\n        \"KILL\": SIGKILL,\n        \"PIPE\": SIGPIPE,\n        \"POLL\": SIGPOLL,\n        \"PROF\": SIGPROF,\n        \"PWR\": SIGPWR,\n        \"QUIT\": SIGQUIT,\n        \"SEGV\": SIGSEGV,\n        \"STKFLT\": SIGSTKFLT,\n        \"STOP\": SIGSTOP,\n        \"SYS\": SIGSYS,\n        \"TERM\": SIGTERM,\n        \"TRAP\": SIGTRAP,\n        \"TSTP\": SIGTSTP,\n        \"TTIN\": SIGTTIN,\n        \"TTOU\": SIGTTOU,\n        \"URG\": SIGURG,\n        \"USR1\": SIGUSR1,\n        \"USR2\": SIGUSR2,\n        \"VTALRM\": SIGVTALRM,\n        \"WINCH\": SIGWINCH,\n        \"XCPU\": SIGXCPU,\n        \"XFSZ\": SIGXFSZ,\n    ]\n}\n\n#endif\n"
  },
  {
    "path": "Sources/ContainerizationOS/Socket/BidirectionalRelay.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport Foundation\nimport Logging\nimport Synchronization\n\n/// Manages bidirectional data relay between two file descriptors using `DispatchSource`.\npublic final class BidirectionalRelay: Sendable {\n    private let fd1: Int32\n    private let fd2: Int32\n    private let log: Logger?\n    private let queue: DispatchQueue\n\n    // `DispatchSourceRead` is thread-safe.\n    private struct ConnectionSources: @unchecked Sendable {\n        let source1: DispatchSourceRead\n        let source2: DispatchSourceRead\n    }\n\n    private enum CompletionState {\n        case pending\n        case waiting(CheckedContinuation<Void, Never>)\n        case completed\n    }\n\n    private let state: Mutex<ConnectionSources?>\n    private let completionState: Mutex<CompletionState>\n\n    // The buffers aren't used concurrently.\n    private nonisolated(unsafe) let buffer1: UnsafeMutableBufferPointer<UInt8>\n    private nonisolated(unsafe) let buffer2: UnsafeMutableBufferPointer<UInt8>\n\n    /// Creates a new bidirectional relay between two file descriptors.\n    ///\n    /// - Parameters:\n    ///   - fd1: The first file descriptor.\n    ///   - fd2: The second file descriptor.\n    ///   - queue: The dispatch queue to use for I/O operations. If nil, a new queue is created.\n    ///   - log: The optional logger for debugging.\n    public init(\n        fd1: Int32,\n        fd2: Int32,\n        queue: DispatchQueue? = nil,\n        log: Logger? = nil\n    ) {\n        self.fd1 = fd1\n        self.fd2 = fd2\n        self.queue = queue ?? DispatchQueue(label: \"com.apple.containerization.bidirectional-relay\")\n        self.log = log\n        self.state = Mutex(nil)\n        self.completionState = Mutex(.pending)\n\n        let pageSize = Int(getpagesize())\n        self.buffer1 = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: pageSize)\n        self.buffer2 = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: pageSize)\n    }\n\n    deinit {\n        buffer1.deallocate()\n        buffer2.deallocate()\n    }\n\n    /// Starts the bidirectional relay to copy data from fd1 to fd2 and from fd2 to fd1.\n    public func start() {\n        let source1 = DispatchSource.makeReadSource(fileDescriptor: fd1, queue: queue)\n        let source2 = DispatchSource.makeReadSource(fileDescriptor: fd2, queue: queue)\n        state.withLock {\n            $0 = ConnectionSources(source1: source1, source2: source2)\n        }\n\n        source1.setEventHandler { [self] in\n            self.fdCopyHandler(\n                buffer: self.buffer1,\n                source: source1,\n                from: self.fd1,\n                to: self.fd2\n            )\n        }\n\n        source2.setEventHandler { [self] in\n            self.fdCopyHandler(\n                buffer: self.buffer2,\n                source: source2,\n                from: self.fd2,\n                to: self.fd1\n            )\n        }\n\n        // Only close underlying fds when both sources are at EOF.\n        // Ensure that one of the cancel handlers will see both sources cancelled.\n        source1.setCancelHandler { [self] in\n            self.log?.debug(\n                \"source1 cancel received\",\n                metadata: [\"fd1\": \"\\(self.fd1)\", \"fd2\": \"\\(self.fd2)\"]\n            )\n\n            self.state.withLock { _ in\n                if source2.isCancelled {\n                    self.closeBothFds()\n                }\n            }\n        }\n\n        source2.setCancelHandler { [self] in\n            self.log?.debug(\n                \"source2 cancel received\",\n                metadata: [\"fd1\": \"\\(self.fd1)\", \"fd2\": \"\\(self.fd2)\"]\n            )\n\n            self.state.withLock { _ in\n                if source1.isCancelled {\n                    self.closeBothFds()\n                }\n            }\n        }\n\n        source1.activate()\n        source2.activate()\n    }\n\n    /// Stops the relay and closes both file descriptors.\n    public func stop() {\n        state.withLock { sources in\n            sources?.source1.cancel()\n            sources?.source2.cancel()\n            sources = nil\n        }\n    }\n\n    /// Waits for the relay to complete.\n    public func waitForCompletion() async {\n        await withCheckedContinuation { c in\n            completionState.withLock { state in\n                switch state {\n                case .pending:\n                    state = .waiting(c)\n                case .waiting:\n                    fatalError(\"waitForCompletion called multiple times\")\n                case .completed:\n                    c.resume()\n                }\n            }\n        }\n    }\n\n    private func fdCopyHandler(\n        buffer: UnsafeMutableBufferPointer<UInt8>,\n        source: DispatchSourceRead,\n        from sourceFd: Int32,\n        to destinationFd: Int32\n    ) {\n        if source.data == 0 {\n            log?.debug(\n                \"source EOF\",\n                metadata: [\n                    \"sourceFd\": \"\\(sourceFd)\",\n                    \"destinationFd\": \"\\(destinationFd)\",\n                ]\n            )\n            if !source.isCancelled {\n                log?.debug(\n                    \"canceling DispatchSourceRead\",\n                    metadata: [\n                        \"sourceFd\": \"\\(sourceFd)\",\n                        \"destinationFd\": \"\\(destinationFd)\",\n                    ]\n                )\n                source.cancel()\n                if shutdown(destinationFd, Int32(SHUT_WR)) != 0 {\n                    log?.debug(\n                        \"failed to shut down writes\",\n                        metadata: [\n                            \"errno\": \"\\(errno)\",\n                            \"sourceFd\": \"\\(sourceFd)\",\n                            \"destinationFd\": \"\\(destinationFd)\",\n                        ]\n                    )\n                }\n            }\n            return\n        }\n\n        do {\n            log?.trace(\n                \"source copy\",\n                metadata: [\n                    \"sourceFd\": \"\\(sourceFd)\",\n                    \"destinationFd\": \"\\(destinationFd)\",\n                    \"size\": \"\\(source.data)\",\n                ]\n            )\n            try Self.fileDescriptorCopy(\n                buffer: buffer,\n                size: source.data,\n                from: sourceFd,\n                to: destinationFd\n            )\n        } catch {\n            log?.warning(\n                \"file descriptor copy failed\",\n                metadata: [\n                    \"error\": \"\\(error)\",\n                    \"sourceFd\": \"\\(sourceFd)\",\n                    \"destinationFd\": \"\\(destinationFd)\",\n                ]\n            )\n            if !source.isCancelled {\n                source.cancel()\n                if shutdown(destinationFd, Int32(SHUT_RDWR)) != 0 {\n                    log?.warning(\n                        \"failed to shut down destination after I/O error\",\n                        metadata: [\n                            \"errno\": \"\\(errno)\",\n                            \"sourceFd\": \"\\(sourceFd)\",\n                            \"destinationFd\": \"\\(destinationFd)\",\n                        ]\n                    )\n                }\n            }\n        }\n    }\n\n    private static func fileDescriptorCopy(\n        buffer: UnsafeMutableBufferPointer<UInt8>,\n        size: UInt,\n        from sourceFd: Int32,\n        to destinationFd: Int32\n    ) throws {\n        let bufferSize = buffer.count\n        var readBytesRemaining = min(Int(size), bufferSize)\n\n        guard let baseAddr = buffer.baseAddress else {\n            throw ContainerizationError(\n                .invalidState,\n                message: \"buffer has no base address\"\n            )\n        }\n\n        while readBytesRemaining > 0 {\n            let readResult = read(sourceFd, baseAddr, min(bufferSize, readBytesRemaining))\n            if readResult <= 0 {\n                throw ContainerizationError(\n                    .internalError,\n                    message: \"zero byte read or error in socket relay: fd \\(sourceFd), result \\(readResult)\"\n                )\n            }\n            readBytesRemaining -= readResult\n\n            var writeBytesRemaining = readResult\n            var writeOffset = 0\n            while writeBytesRemaining > 0 {\n                let writeResult = write(destinationFd, baseAddr.advanced(by: writeOffset), writeBytesRemaining)\n                if writeResult <= 0 {\n                    throw ContainerizationError(\n                        .internalError,\n                        message: \"zero byte write or error in socket relay: fd \\(destinationFd), result \\(writeResult)\"\n                    )\n                }\n                writeBytesRemaining -= writeResult\n                writeOffset += writeResult\n            }\n        }\n    }\n\n    private func closeBothFds() {\n        log?.debug(\n            \"close file descriptors\",\n            metadata: [\"fd1\": \"\\(fd1)\", \"fd2\": \"\\(fd2)\"]\n        )\n        close(fd1)\n        close(fd2)\n        completionState.withLock { state in\n            if case .waiting(let c) = state {\n                c.resume()\n            }\n            state = .completed\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/Socket/Socket.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport CShim\nimport Foundation\nimport Synchronization\n\n#if canImport(Musl)\nimport Musl\n#elseif canImport(Glibc)\nimport Glibc\n#elseif canImport(Darwin)\nimport Darwin\n#else\n#error(\"Socket not supported on this platform.\")\n#endif\n\n#if !os(Windows)\nlet sysFchmod = fchmod\nlet sysRead = read\nlet sysUnlink = unlink\nlet sysSend = send\nlet sysClose = close\nlet sysShutdown = shutdown\nlet sysBind = bind\nlet sysSocket = socket\nlet sysSetsockopt = setsockopt\nlet sysGetsockopt = getsockopt\nlet sysListen = listen\nlet sysAccept = accept\nlet sysConnect = connect\nlet sysIoctl: @convention(c) (CInt, CUnsignedLong, UnsafeMutableRawPointer) -> CInt = ioctl\nlet sysRecvmsg = recvmsg\n#endif\n\n/// Thread-safe socket wrapper.\npublic final class Socket: Sendable {\n    public enum TimeoutOption {\n        case send\n        case receive\n    }\n\n    public enum ShutdownOption {\n        case read\n        case write\n        case readWrite\n    }\n\n    private enum SocketState {\n        case created\n        case connected\n        case listening\n    }\n\n    private struct State {\n        let socketState: SocketState\n        let handle: FileHandle?\n        let type: SocketType\n        let acceptSource: DispatchSourceRead?\n    }\n\n    private let _closeOnDeinit: Bool\n    private let _queue: DispatchQueue\n\n    private let state: Mutex<State>\n\n    public var fileDescriptor: Int32 {\n        guard let handle = state.withLock({ $0.handle }) else {\n            return -1\n        }\n        return handle.fileDescriptor\n    }\n\n    public convenience init(type: SocketType, closeOnDeinit: Bool = true) throws {\n        let sockFD = sysSocket(type.domain, type.type, 0)\n        if sockFD < 0 {\n            throw SocketError.withErrno(\"failed to create socket: \\(sockFD)\", errno: errno)\n        }\n        self.init(fd: sockFD, type: type, closeOnDeinit: closeOnDeinit)\n    }\n\n    init(fd: Int32, type: SocketType, closeOnDeinit: Bool) {\n        _queue = DispatchQueue(label: \"com.apple.containerization.socket\")\n        _closeOnDeinit = closeOnDeinit\n        let state = State(\n            socketState: .created,\n            handle: FileHandle(fileDescriptor: fd, closeOnDealloc: false),\n            type: type,\n            acceptSource: nil\n        )\n        self.state = Mutex(state)\n    }\n\n    /// Internal initializer for wrapping already-connected file descriptors (e.g., from socketpair)\n    /// Ideally we just get rid of the state machine in this class. Not sure how much value it provides..\n    init(fd: Int32, type: SocketType, closeOnDeinit: Bool, connected: Bool) {\n        _queue = DispatchQueue(label: \"com.apple.containerization.socket\")\n        _closeOnDeinit = closeOnDeinit\n        let state = State(\n            socketState: connected ? .connected : .created,\n            handle: FileHandle(fileDescriptor: fd, closeOnDealloc: false),\n            type: type,\n            acceptSource: nil\n        )\n        self.state = Mutex(state)\n    }\n\n    deinit {\n        if _closeOnDeinit {\n            try? close()\n        }\n    }\n}\n\nextension Socket {\n    static func errnoToError(msg: String) -> SocketError {\n        SocketError.withErrno(\"\\(msg) (\\(_errnoString(errno)))\", errno: errno)\n    }\n\n    public func connect() throws {\n        try state.withLock { currentState in\n            guard currentState.socketState == .created else {\n                throw SocketError.invalidOperationOnSocket(\"connect\")\n            }\n            guard let handle = currentState.handle else {\n                throw SocketError.closed\n            }\n\n            var res: Int32 = 0\n            try currentState.type.withSockAddr { (ptr, length) in\n                res = Syscall.retrying {\n                    sysConnect(handle.fileDescriptor, ptr, length)\n                }\n            }\n\n            if res == -1 {\n                throw Socket.errnoToError(msg: \"could not connect to socket \\(currentState.type)\")\n            }\n\n            currentState = State(\n                socketState: .connected,\n                handle: handle,\n                type: currentState.type,\n                acceptSource: currentState.acceptSource\n            )\n        }\n    }\n\n    public func listen() throws {\n        try state.withLock { currentState in\n            guard currentState.socketState == .created else {\n                throw SocketError.invalidOperationOnSocket(\"listen\")\n            }\n            guard let handle = currentState.handle else {\n                throw SocketError.closed\n            }\n\n            try currentState.type.beforeBind(fd: handle.fileDescriptor)\n\n            var rc: Int32 = 0\n            try currentState.type.withSockAddr { (ptr, length) in\n                rc = sysBind(handle.fileDescriptor, ptr, length)\n            }\n\n            if rc < 0 {\n                throw Socket.errnoToError(msg: \"could not bind to \\(currentState.type)\")\n            }\n\n            try currentState.type.beforeListen(fd: handle.fileDescriptor)\n\n            if sysListen(handle.fileDescriptor, SOMAXCONN) < 0 {\n                throw Socket.errnoToError(msg: \"listen failed on \\(currentState.type)\")\n            }\n\n            currentState = State(\n                socketState: .listening,\n                handle: handle,\n                type: currentState.type,\n                acceptSource: currentState.acceptSource\n            )\n        }\n    }\n\n    public func close() throws {\n        try state.withLock { currentState in\n            guard let handle = currentState.handle else {\n                // Already closed.\n                return\n            }\n\n            let acceptSource = currentState.acceptSource\n\n            acceptSource?.cancel()\n            try handle.close()\n\n            currentState = State(\n                socketState: currentState.socketState,\n                handle: nil,\n                type: currentState.type,\n                acceptSource: nil\n            )\n        }\n    }\n\n    public func write(data: any DataProtocol) throws -> Int {\n        let handle = try state.withLock { currentState in\n            guard currentState.socketState == .connected else {\n                throw SocketError.invalidOperationOnSocket(\"write\")\n            }\n            guard let handle = currentState.handle else {\n                throw SocketError.closed\n            }\n            return handle\n        }\n\n        if data.isEmpty {\n            return 0\n        }\n\n        try handle.write(contentsOf: data)\n        return data.count\n    }\n\n    public func acceptStream(closeOnDeinit: Bool = true) throws -> AsyncThrowingStream<Socket, Swift.Error> {\n        let source = try state.withLock { currentState -> DispatchSourceRead in\n            guard currentState.socketState == .listening else {\n                throw SocketError.invalidOperationOnSocket(\"accept\")\n            }\n            guard let handle = currentState.handle else {\n                throw SocketError.closed\n            }\n            guard currentState.acceptSource == nil else {\n                throw SocketError.acceptStreamExists\n            }\n\n            let source = DispatchSource.makeReadSource(\n                fileDescriptor: handle.fileDescriptor,\n                queue: _queue\n            )\n\n            currentState = State(\n                socketState: currentState.socketState,\n                handle: handle,\n                type: currentState.type,\n                acceptSource: source\n            )\n\n            return source\n        }\n\n        return AsyncThrowingStream { cont in\n            source.setCancelHandler {\n                cont.finish()\n            }\n            source.setEventHandler(handler: {\n                if source.data == 0 {\n                    source.cancel()\n                    return\n                }\n\n                do {\n                    let connection = try self.accept(closeOnDeinit: closeOnDeinit)\n                    cont.yield(connection)\n                } catch SocketError.closed {\n                    source.cancel()\n                } catch {\n                    cont.yield(with: .failure(error))\n                    source.cancel()\n                }\n            })\n            source.activate()\n        }\n    }\n\n    public func accept(closeOnDeinit: Bool = true) throws -> Socket {\n        let (handle, socketType) = try state.withLock { currentState in\n            guard currentState.socketState == .listening else {\n                throw SocketError.invalidOperationOnSocket(\"accept\")\n            }\n            guard let handle = currentState.handle else {\n                throw SocketError.closed\n            }\n            return (handle, currentState.type)\n        }\n\n        let (clientFD, newSocketType) = try socketType.accept(fd: handle.fileDescriptor)\n        return Socket(\n            fd: clientFD,\n            type: newSocketType,\n            closeOnDeinit: closeOnDeinit,\n            connected: true\n        )\n    }\n\n    /// Receive a file descriptor via SCM_RIGHTS control message.\n    /// This is commonly used for passing file descriptors between processes via Unix domain sockets.\n    public func receiveFileDescriptor() throws -> Int32 {\n        let handle = try state.withLock { currentState in\n            guard currentState.socketState == .connected else {\n                throw SocketError.invalidOperationOnSocket(\"receiveFileDescriptor\")\n            }\n            guard let handle = currentState.handle else {\n                throw SocketError.closed\n            }\n            return handle\n        }\n\n        var msg = msghdr()\n        var iov = iovec()\n        var buf: UInt8 = 0\n\n        iov.iov_base = withUnsafeMutablePointer(to: &buf) { UnsafeMutableRawPointer($0) }\n        iov.iov_len = 1\n\n        msg.msg_iov = withUnsafeMutablePointer(to: &iov) { $0 }\n        msg.msg_iovlen = 1\n\n        var cmsgBuf = [UInt8](repeating: 0, count: Int(CZ_CMSG_SPACE(Int(MemoryLayout<Int32>.size))))\n        msg.msg_control = withUnsafeMutablePointer(to: &cmsgBuf[0]) { UnsafeMutableRawPointer($0) }\n\n        #if canImport(Glibc)\n        msg.msg_controllen = size_t(cmsgBuf.count)\n        #else\n        msg.msg_controllen = socklen_t(cmsgBuf.count)\n        #endif\n\n        let recvResult = withUnsafeMutablePointer(to: &msg) { msgPtr in\n            sysRecvmsg(handle.fileDescriptor, msgPtr, 0)\n        }\n\n        guard recvResult >= 0 else {\n            throw Socket.errnoToError(msg: \"recvmsg failed\")\n        }\n\n        // Extract file descriptor from control message\n        let cmsgPtr = withUnsafeMutablePointer(to: &msg) { CZ_CMSG_FIRSTHDR($0) }\n        guard let cmsg = cmsgPtr else {\n            throw SocketError.invalidFileDescriptor\n        }\n\n        guard cmsg.pointee.cmsg_level == SOL_SOCKET,\n            cmsg.pointee.cmsg_type == SCM_RIGHTS\n        else {\n            throw SocketError.invalidFileDescriptor\n        }\n\n        guard let dataPtr = CZ_CMSG_DATA(cmsg) else {\n            throw SocketError.invalidFileDescriptor\n        }\n\n        let fdPtr = dataPtr.assumingMemoryBound(to: Int32.self)\n        let fd = fdPtr.pointee\n        guard fd >= 0 else {\n            throw SocketError.invalidFileDescriptor\n        }\n\n        return fd\n    }\n\n    public func read(buffer: inout Data) throws -> Int {\n        let handle = try state.withLock { currentState in\n            guard currentState.socketState == .connected else {\n                throw SocketError.invalidOperationOnSocket(\"read\")\n            }\n            guard let handle = currentState.handle else {\n                throw SocketError.closed\n            }\n            return handle\n        }\n\n        var bytesRead = 0\n        let bufferSize = buffer.count\n        try buffer.withUnsafeMutableBytes { pointer in\n            guard let baseAddress = pointer.baseAddress else {\n                throw SocketError.missingBaseAddress\n            }\n\n            bytesRead = Syscall.retrying {\n                sysRead(handle.fileDescriptor, baseAddress, bufferSize)\n            }\n            if bytesRead < 0 {\n                throw Socket.errnoToError(msg: \"error reading from connection\")\n            } else if bytesRead == 0 {\n                throw SocketError.closed\n            }\n        }\n        return bytesRead\n    }\n\n    public func shutdown(how: ShutdownOption) throws {\n        let handle = try state.withLock { currentState in\n            guard let handle = currentState.handle else {\n                throw SocketError.closed\n            }\n            return handle\n        }\n\n        var howOpt: Int32 = 0\n        switch how {\n        case .read:\n            howOpt = Int32(SHUT_RD)\n        case .write:\n            howOpt = Int32(SHUT_WR)\n        case .readWrite:\n            howOpt = Int32(SHUT_RDWR)\n        }\n\n        if sysShutdown(handle.fileDescriptor, howOpt) < 0 {\n            throw Socket.errnoToError(msg: \"shutdown failed\")\n        }\n    }\n\n    public func setSockOpt(sockOpt: Int32 = 0, ptr: UnsafeRawPointer, stride: UInt32) throws {\n        let handle = try state.withLock { currentState in\n            guard let handle = currentState.handle else {\n                throw SocketError.closed\n            }\n            return handle\n        }\n\n        if setsockopt(handle.fileDescriptor, SOL_SOCKET, sockOpt, ptr, stride) < 0 {\n            throw Socket.errnoToError(msg: \"failed to set sockopt\")\n        }\n    }\n\n    public func setTimeout(option: TimeoutOption, seconds: Int) throws {\n        let handle = try state.withLock { currentState in\n            guard let handle = currentState.handle else {\n                throw SocketError.closed\n            }\n            return handle\n        }\n\n        var sockOpt: Int32 = 0\n        switch option {\n        case .receive:\n            sockOpt = SO_RCVTIMEO\n        case .send:\n            sockOpt = SO_SNDTIMEO\n        }\n\n        var timer = timeval()\n        timer.tv_sec = seconds\n        timer.tv_usec = 0\n\n        if setsockopt(\n            handle.fileDescriptor,\n            SOL_SOCKET,\n            sockOpt,\n            &timer,\n            socklen_t(MemoryLayout<timeval>.size)\n        ) < 0 {\n            throw Socket.errnoToError(msg: \"failed to set read timeout\")\n        }\n    }\n\n    static func _errnoString(_ err: Int32?) -> String {\n        String(validatingCString: strerror(errno)) ?? \"error: \\(errno)\"\n    }\n}\n\npublic enum SocketError: Error, Equatable, CustomStringConvertible {\n    case closed\n    case acceptStreamExists\n    case invalidOperationOnSocket(String)\n    case missingBaseAddress\n    case withErrno(_ msg: String, errno: Int32)\n    case invalidFileDescriptor\n\n    public var description: String {\n        switch self {\n        case .closed:\n            return \"socket: closed\"\n        case .acceptStreamExists:\n            return \"accept stream already exists\"\n        case .invalidOperationOnSocket(let operation):\n            return \"socket: invalid operation on socket '\\(operation)'\"\n        case .missingBaseAddress:\n            return \"socket: missing base address\"\n        case .withErrno(let msg, _):\n            return \"socket: error \\(msg)\"\n        case .invalidFileDescriptor:\n            return \"socket: invalid file descriptor received\"\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/Socket/SocketType.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if canImport(Musl)\nimport Musl\n#elseif canImport(Glibc)\nimport Glibc\n#elseif canImport(Darwin)\nimport Darwin\n#else\n#error(\"SocketType not supported on this platform.\")\n#endif\n\n/// Protocol used to describe the family of socket to be created with `Socket`.\npublic protocol SocketType: Sendable, CustomStringConvertible {\n    /// The domain for the socket (AF_UNIX, AF_VSOCK etc.)\n    var domain: Int32 { get }\n    /// The type of socket (SOCK_STREAM).\n    var type: Int32 { get }\n\n    /// Actions to perform before calling bind(2).\n    func beforeBind(fd: Int32) throws\n    /// Actions to perform before calling listen(2).\n    func beforeListen(fd: Int32) throws\n\n    /// Handle accept(2) for an implementation of a socket type.\n    func accept(fd: Int32) throws -> (Int32, SocketType)\n    /// Provide a sockaddr pointer (by casting a socket specific type like sockaddr_un for example).\n    func withSockAddr(_ closure: (_ ptr: UnsafePointer<sockaddr>, _ len: UInt32) throws -> Void) throws\n}\n\nextension SocketType {\n    public func beforeBind(fd: Int32) {}\n    public func beforeListen(fd: Int32) {}\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/Socket/UnixType.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if canImport(Musl)\nimport Musl\nlet _SOCK_STREAM = SOCK_STREAM\n#elseif canImport(Glibc)\nimport Glibc\nlet _SOCK_STREAM = Int32(SOCK_STREAM.rawValue)\n#elseif canImport(Darwin)\nimport Darwin\nlet _SOCK_STREAM = SOCK_STREAM\n#else\n#error(\"UnixType not supported on this platform.\")\n#endif\n\n/// Unix domain socket variant of `SocketType`.\npublic struct UnixType: SocketType, Sendable, CustomStringConvertible {\n    public var domain: Int32 { AF_UNIX }\n    public var type: Int32 { _SOCK_STREAM }\n    public var description: String {\n        path\n    }\n\n    public let path: String\n    public let perms: mode_t?\n    private let _addr: sockaddr_un\n    private let _unlinkExisting: Bool\n\n    private init(sockaddr: sockaddr_un) {\n        let pathname: String = withUnsafePointer(to: sockaddr.sun_path) { ptr in\n            let charPtr = UnsafeRawPointer(ptr).assumingMemoryBound(to: CChar.self)\n            return String(cString: charPtr)\n        }\n        self._addr = sockaddr\n        self.path = pathname\n        self._unlinkExisting = false\n        self.perms = nil\n    }\n\n    /// Mode and unlinkExisting only used if the socket is going to be a listening socket.\n    public init(\n        path: String,\n        perms: mode_t? = nil,\n        unlinkExisting: Bool = false\n    ) throws {\n        self.path = path\n        self.perms = perms\n        self._unlinkExisting = unlinkExisting\n        var addr = sockaddr_un()\n        addr.sun_family = sa_family_t(AF_UNIX)\n\n        let socketName = path\n        let nameLength = socketName.utf8.count\n\n        #if os(macOS)\n        // Funnily enough, this isn't limited by sun path on macOS even though\n        // it's stated as so.\n        let lengthLimit = 253\n        #elseif os(Linux)\n        let lengthLimit = MemoryLayout.size(ofValue: addr.sun_path)\n        #endif\n\n        guard nameLength < lengthLimit else {\n            throw Error.nameTooLong(path)\n        }\n\n        _ = withUnsafeMutablePointer(to: &addr.sun_path.0) { ptr in\n            socketName.withCString { strncpy(ptr, $0, nameLength) }\n        }\n\n        #if os(macOS)\n        addr.sun_len = UInt8(MemoryLayout<UInt8>.size + MemoryLayout<sa_family_t>.size + socketName.utf8.count + 1)\n        #endif\n        self._addr = addr\n    }\n\n    public func accept(fd: Int32) throws -> (Int32, SocketType) {\n        var clientFD: Int32 = -1\n        var addr = sockaddr_un()\n\n        clientFD = Syscall.retrying {\n            var size = socklen_t(MemoryLayout<sockaddr_un>.stride)\n            return withUnsafeMutablePointer(to: &addr) { pointer in\n                pointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { pointer in\n                    sysAccept(fd, pointer, &size)\n                }\n            }\n        }\n        if clientFD < 0 {\n            throw Socket.errnoToError(msg: \"accept failed\")\n        }\n\n        return (clientFD, UnixType(sockaddr: addr))\n    }\n\n    public func beforeBind(fd: Int32) throws {\n        #if os(Linux)\n        // Only Linux supports setting the mode of a socket before binding.\n        if let perms = self.perms {\n            guard fchmod(fd, perms) == 0 else {\n                throw Socket.errnoToError(msg: \"fchmod failed\")\n            }\n        }\n        #endif\n\n        var rc: Int32 = 0\n        if self._unlinkExisting {\n            rc = sysUnlink(self.path)\n            if rc != 0 && errno != ENOENT {\n                throw Socket.errnoToError(msg: \"failed to remove old socket at \\(self.path)\")\n            }\n        }\n    }\n\n    public func beforeListen(fd: Int32) throws {\n        #if os(macOS)\n        if let perms = self.perms {\n            guard chmod(self.path, perms) == 0 else {\n                throw Socket.errnoToError(msg: \"chmod failed\")\n            }\n        }\n        #endif\n    }\n\n    public func withSockAddr(_ closure: (UnsafePointer<sockaddr>, UInt32) throws -> Void) throws {\n        var addr = self._addr\n        try withUnsafePointer(to: &addr) {\n            let addrBytes = UnsafeRawPointer($0).assumingMemoryBound(to: sockaddr.self)\n            try closure(addrBytes, UInt32(MemoryLayout<sockaddr_un>.stride))\n        }\n    }\n}\n\nextension UnixType {\n    /// `UnixType` errors.\n    public enum Error: Swift.Error, CustomStringConvertible {\n        case nameTooLong(_: String)\n\n        public var description: String {\n            switch self {\n            case .nameTooLong(let name):\n                return \"\\(name) is too long for a Unix Domain Socket path\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/Socket/VsockType.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport CShim\n\n#if canImport(Musl)\nimport Musl\n#elseif canImport(Glibc)\nimport Glibc\n#elseif canImport(Darwin)\nimport Darwin\n#else\n#error(\"VsockType not supported on this platform.\")\n#endif\n\n/// Vsock variant of `SocketType`.\npublic struct VsockType: SocketType, Sendable {\n    public var domain: Int32 { AF_VSOCK }\n    public var type: Int32 { _SOCK_STREAM }\n    public var description: String {\n        \"\\(cid):\\(port)\"\n    }\n\n    public static let anyCID: UInt32 = UInt32(bitPattern: -1)\n    public static let hypervisorCID: UInt32 = 0x0\n    // Supported on Linux 5.6+; otherwise, will need to use getLocalCID().\n    public static let localCID: UInt32 = 0x1\n    public static let hostCID: UInt32 = 0x2\n\n    // socketFD is unused on Linux.\n    public static func getLocalCID(socketFD: Int32) throws -> UInt32 {\n        let request = VsockLocalCIDIoctl\n        #if os(Linux)\n        let fd = open(\"/dev/vsock\", O_RDONLY | O_CLOEXEC)\n        if fd == -1 {\n            throw Socket.errnoToError(msg: \"failed to open /dev/vsock\")\n        }\n        defer { close(fd) }\n        #else\n        let fd = socketFD\n        #endif\n        var cid: UInt32 = 0\n        guard sysIoctl(fd, numericCast(request), &cid) != -1 else {\n            throw Socket.errnoToError(msg: \"failed to get local cid\")\n        }\n        return cid\n    }\n\n    public let port: UInt32\n    public let cid: UInt32\n\n    private let _addr: sockaddr_vm\n\n    public init(port: UInt32, cid: UInt32) {\n        self.cid = cid\n        self.port = port\n        var sockaddr = sockaddr_vm()\n        sockaddr.svm_family = sa_family_t(AF_VSOCK)\n        sockaddr.svm_cid = cid\n        sockaddr.svm_port = port\n        self._addr = sockaddr\n    }\n\n    private init(sockaddr: sockaddr_vm) {\n        self._addr = sockaddr\n        self.cid = sockaddr.svm_cid\n        self.port = sockaddr.svm_port\n    }\n\n    public func accept(fd: Int32) throws -> (Int32, SocketType) {\n        var clientFD: Int32 = -1\n        var addr = sockaddr_vm()\n\n        while clientFD < 0 {\n            var size = socklen_t(MemoryLayout<sockaddr_vm>.stride)\n            clientFD = withUnsafeMutablePointer(to: &addr) { pointer in\n                pointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { pointer in\n                    sysAccept(fd, pointer, &size)\n                }\n            }\n            if clientFD < 0 && errno != EINTR {\n                throw Socket.errnoToError(msg: \"accept failed\")\n            }\n        }\n        return (clientFD, VsockType(sockaddr: addr))\n    }\n\n    public func withSockAddr(_ closure: (UnsafePointer<sockaddr>, UInt32) throws -> Void) throws {\n        var addr = self._addr\n        try withUnsafePointer(to: &addr) {\n            let addrBytes = UnsafeRawPointer($0).assumingMemoryBound(to: sockaddr.self)\n            try closure(addrBytes, UInt32(MemoryLayout<sockaddr_vm>.stride))\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/Syscall.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if canImport(Musl)\nimport Musl\n#elseif canImport(Glibc)\nimport Glibc\n#elseif canImport(Darwin)\nimport Darwin\n#else\n#error(\"retryingSyscall not supported on this platform.\")\n#endif\n\n/// Helper type to deal with running system calls.\npublic struct Syscall {\n    /// Retry a syscall on EINTR.\n    public static func retrying<T: FixedWidthInteger>(_ closure: () -> T) -> T {\n        while true {\n            let res = closure()\n            if res == -1 && errno == EINTR {\n                continue\n            }\n            return res\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/Sysctl.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// Helper type to deal with system control functionalities.\npublic struct Sysctl {\n    #if os(macOS)\n    /// Simple `sysctlbyname` wrapper.\n    public static func byName(_ name: String) throws -> Int64 {\n        var num: Int64 = 0\n        var size = MemoryLayout<Int64>.size\n        if sysctlbyname(name, &num, &size, nil, 0) != 0 {\n            throw POSIXError.fromErrno()\n        }\n        return num\n    }\n    #endif\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/Terminal.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// `Terminal` provides a clean interface to deal with terminal interactions on Unix platforms.\npublic struct Terminal: Sendable {\n    private let initState: termios?\n\n    private var descriptor: Int32 {\n        handle.fileDescriptor\n    }\n    public let handle: FileHandle\n\n    public init(descriptor: Int32, setInitState: Bool = true) throws {\n        if setInitState {\n            self.initState = try Self.getattr(descriptor)\n        } else {\n            initState = nil\n        }\n        self.handle = .init(fileDescriptor: descriptor, closeOnDealloc: false)\n    }\n\n    /// Write the provided data to the tty device.\n    public func write(_ data: Data) throws {\n        try handle.write(contentsOf: data)\n    }\n\n    /// The winsize for a pty.\n    public struct Size: Sendable {\n        let size: winsize\n\n        /// The width or `col` of the pty.\n        public var width: UInt16 {\n            size.ws_col\n        }\n        /// The height or `rows` of the pty.\n        public var height: UInt16 {\n            size.ws_row\n        }\n\n        init(_ size: winsize) {\n            self.size = size\n        }\n\n        /// Set the size for use with a pty.\n        public init(width cols: UInt16, height rows: UInt16) {\n            self.size = winsize(ws_row: rows, ws_col: cols, ws_xpixel: 0, ws_ypixel: 0)\n        }\n    }\n\n    /// Return the current pty attached to any of the STDIO descriptors.\n    public static var current: Terminal {\n        get throws {\n            for i in [STDERR_FILENO, STDOUT_FILENO, STDIN_FILENO] {\n                do {\n                    return try Terminal(descriptor: i)\n                } catch {}\n            }\n            throw Error.notAPty\n        }\n    }\n\n    /// The current window size for the pty.\n    public var size: Size {\n        get throws {\n            var ws = winsize()\n            try fromSyscall(ioctl(descriptor, UInt(TIOCGWINSZ), &ws))\n            return Size(ws)\n        }\n    }\n\n    /// Create a new pty pair.\n    /// - Parameter initialSize: An initial size of the child pty.\n    public static func create(initialSize: Size? = nil) throws -> (parent: Terminal, child: Terminal) {\n        var parent: Int32 = 0\n        var child: Int32 = 0\n        let size = initialSize ?? Size(width: 120, height: 40)\n        var ws = size.size\n\n        try fromSyscall(openpty(&parent, &child, nil, nil, &ws))\n        return (\n            parent: try Terminal(descriptor: parent, setInitState: false),\n            child: try Terminal(descriptor: child, setInitState: false)\n        )\n    }\n}\n\n// MARK: Errors\n\nextension Terminal {\n    public enum Error: Swift.Error, CustomStringConvertible {\n        case notAPty\n\n        public var description: String {\n            switch self {\n            case .notAPty:\n                return \"the provided fd is not a pty\"\n            }\n        }\n    }\n}\n\nextension Terminal {\n    /// Resize the current pty from the size of the provided pty.\n    ///  - Parameter pty: A pty to resize from.\n    public func resize(from pty: Terminal) throws {\n        var ws = try pty.size\n        try fromSyscall(ioctl(descriptor, UInt(TIOCSWINSZ), &ws))\n    }\n\n    /// Resize the pty to the provided window size.\n    ///  - Parameter size: A window size for a pty.\n    public func resize(size: Size) throws {\n        var ws = size.size\n        try fromSyscall(ioctl(descriptor, UInt(TIOCSWINSZ), &ws))\n    }\n\n    /// Resize the pty to the provided window size.\n    /// - Parameter width: A width or cols of the terminal.\n    /// - Parameter height: A height or rows of the terminal.\n    public func resize(width: UInt16, height: UInt16) throws {\n        var ws = Size(width: width, height: height)\n        try fromSyscall(ioctl(descriptor, UInt(TIOCSWINSZ), &ws))\n    }\n}\n\nextension Terminal {\n    /// Enable raw mode for the pty.\n    public func setraw() throws {\n        var attr = try Self.getattr(descriptor)\n        cfmakeraw(&attr)\n        attr.c_oflag = attr.c_oflag | tcflag_t(OPOST)\n        try fromSyscall(tcsetattr(descriptor, TCSANOW, &attr))\n    }\n\n    /// Enable echo support.\n    /// Chars typed will be displayed to the terminal.\n    public func enableEcho() throws {\n        var attr = try Self.getattr(descriptor)\n        attr.c_iflag &= ~tcflag_t(ICRNL)\n        attr.c_lflag &= ~tcflag_t(ICANON | ECHO)\n        try fromSyscall(tcsetattr(descriptor, TCSANOW, &attr))\n    }\n\n    /// Disable echo support.\n    /// Chars typed will not be displayed back to the terminal.\n    public func disableEcho() throws {\n        var attr = try Self.getattr(descriptor)\n        attr.c_lflag &= ~tcflag_t(ECHO)\n        try fromSyscall(tcsetattr(descriptor, TCSANOW, &attr))\n    }\n\n    private static func getattr(_ fd: Int32) throws -> termios {\n        var attr = termios()\n        try fromSyscall(tcgetattr(fd, &attr))\n        return attr\n    }\n}\n\n// MARK: Reset\n\nextension Terminal {\n    /// Close this pty's file descriptor.\n    public func close() throws {\n        do {\n            // Use FileHandle's close directly as it sets the underlying fd in the object\n            // to -1 for us.\n            try self.handle.close()\n        } catch {\n            if let error = error as NSError?, error.domain == NSPOSIXErrorDomain {\n                throw POSIXError(.init(rawValue: Int32(error.code))!)\n            }\n            throw error\n        }\n    }\n\n    /// Reset the pty to its initial state.\n    public func reset() throws {\n        if var attr = initState {\n            try fromSyscall(tcsetattr(descriptor, TCSANOW, &attr))\n        }\n    }\n\n    /// Reset the pty to its initial state masking any errors.\n    /// This is commonly used in a `defer` body to reset the current pty where the error code is not generally useful.\n    public func tryReset() {\n        try? reset()\n    }\n}\n\nprivate func fromSyscall(_ status: Int32) throws {\n    guard status == 0 else {\n        throw POSIXError(.init(rawValue: errno)!)\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/URL+Extensions.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\n/// The `resolvingSymlinksInPath` method of the `URL` struct does not resolve the symlinks\n/// for directories under `/private` which include`tmp`, `var` and `etc`\n/// hence adding a method to build up on the existing `resolvingSymlinksInPath` that prepends `/private` to those paths\nextension URL {\n    /// returns the unescaped absolutePath of a URL joined by separator\n    func absolutePath(_ separator: String = \"/\") -> String {\n        self.pathComponents\n            .joined(separator: separator)\n            .dropFirst(\"/\".count)\n            .description\n    }\n\n    public func resolvingSymlinksInPathWithPrivate() -> URL {\n        let url = self.resolvingSymlinksInPath()\n        #if os(macOS)\n        let parts = url.pathComponents\n        if parts.count > 1 {\n            if (parts.first == \"/\") && [\"tmp\", \"var\", \"etc\"].contains(parts[1]) {\n                if let resolved = NSURL.fileURL(withPathComponents: [\"/\", \"private\"] + parts[1...]) {\n                    return resolved\n                }\n            }\n        }\n        #endif\n        return url\n    }\n\n    public var isDirectory: Bool {\n        (try? resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory == true\n    }\n\n    public var isSymlink: Bool {\n        (try? resourceValues(forKeys: [.isSymbolicLinkKey]))?.isSymbolicLink == true\n    }\n}\n"
  },
  {
    "path": "Sources/ContainerizationOS/User.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport Foundation\n\n/// `User` provides utilities to ensure that a given username exists in\n/// /etc/passwd (and /etc/group). Largely inspired by runc (and moby's)\n/// `user` packages.\npublic enum User {\n    public static let passwdFilePath = URL(filePath: \"/etc/passwd\")\n    public static let groupFilePath = URL(filePath: \"/etc/group\")\n\n    private static let minID: UInt32 = 0\n    private static let maxID: UInt32 = 2_147_483_647\n\n    public struct ExecUser: Sendable {\n        public var uid: UInt32\n        public var gid: UInt32\n        public var sgids: [UInt32]\n        public var home: String\n        public var shell: String\n\n        public init(uid: UInt32, gid: UInt32, sgids: [UInt32], home: String, shell: String) {\n            self.uid = uid\n            self.gid = gid\n            self.sgids = sgids\n            self.home = home\n            self.shell = shell\n        }\n    }\n\n    public struct User {\n        public var name: String\n        public var password: String\n        public var uid: UInt32\n        public var gid: UInt32\n        public var gecos: String\n        public var home: String\n        public var shell: String\n\n        /// The argument `rawString` must follow the below format.\n        /// Name:Password:Uid:Gid:Gecos:Home:Shell\n        init(rawString: String) throws {\n            let args = rawString.split(separator: \":\", omittingEmptySubsequences: false)\n            guard args.count == 7 else {\n                throw Error.parseError(\"cannot parse User from '\\(rawString)'\")\n            }\n            guard let uid = UInt32(args[2]) else {\n                throw Error.parseError(\"cannot parse uid from '\\(args[2])'\")\n            }\n            guard let gid = UInt32(args[3]) else {\n                throw Error.parseError(\"cannot parse gid from '\\(args[3])'\")\n            }\n            self.name = String(args[0])\n            self.password = String(args[1])\n            self.uid = uid\n            self.gid = gid\n            self.gecos = String(args[4])\n            self.home = String(args[5])\n            self.shell = String(args[6])\n        }\n    }\n\n    struct Group {\n        var name: String\n        var password: String\n        var gid: UInt32\n        var users: [String]\n\n        /// The argument `rawString` must follow the below format.\n        /// Name:Password:Gid:user1,user2\n        init(rawString: String) throws {\n            let args = rawString.split(separator: \":\", omittingEmptySubsequences: false)\n            guard args.count == 4 else {\n                throw Error.parseError(\"cannot parse Group from '\\(rawString)'\")\n            }\n            guard let gid = UInt32(args[2]) else {\n                throw Error.parseError(\"cannot parse gid from '\\(args[2])'\")\n            }\n            self.name = String(args[0])\n            self.password = String(args[1])\n            self.gid = gid\n            self.users = args[3].split(separator: \",\").map { String($0) }\n        }\n    }\n}\n\n// MARK: Private methods\n\nextension User {\n    private static func parse(file: URL, handler: (_ line: String) throws -> Void) throws {\n        let fm = FileManager.default\n        guard fm.fileExists(atPath: file.absolutePath()) else {\n            throw Error.missingFile(file.absolutePath())\n        }\n        let content = try String(contentsOf: file, encoding: .ascii)\n        let lines = content.components(separatedBy: .newlines)\n        for line in lines {\n            guard !line.isEmpty else {\n                continue\n            }\n            try handler(line.trimmingCharacters(in: .whitespaces))\n        }\n    }\n\n    /// Parse the contents of the passwd file with a provided filter function.\n    static func parsePasswd(passwdFile: URL, filter: ((User) -> Bool)? = nil) throws -> [User] {\n        var users: [User] = []\n        try self.parse(file: passwdFile) { line in\n            let user = try User(rawString: line)\n            if let filter {\n                guard filter(user) else {\n                    return\n                }\n            }\n            users.append(user)\n        }\n        return users\n    }\n\n    /// Parse the contents of the group file with a provided filter function.\n    static func parseGroup(groupFile: URL, filter: ((Group) -> Bool)? = nil) throws -> [Group] {\n        var groups: [Group] = []\n        try self.parse(file: groupFile) { line in\n            let group = try Group(rawString: line)\n            if let filter {\n                guard filter(group) else {\n                    return\n                }\n            }\n            groups.append(group)\n        }\n        return groups\n    }\n}\n\n// MARK: Public methods\n\nextension User {\n    /// Looks up uid in the password file specified by `passwdPath`.\n    public static func lookupUid(passwdPath: URL = Self.passwdFilePath, uid: UInt32) throws -> User {\n        let users = try parsePasswd(\n            passwdFile: passwdPath,\n            filter: { u in\n                u.uid == uid\n            })\n        if users.count == 0 {\n            throw Error.noPasswdEntries\n        }\n        return users[0]\n    }\n\n    /// Parses a user string in any of the following formats:\n    /// \"user, uid, user:group, uid:gid, uid:group, user:gid\"\n    /// and returns an ExecUser type from the information.\n    public static func getExecUser(\n        userString: String,\n        defaults: ExecUser? = nil,\n        passwdPath: URL = Self.passwdFilePath,\n        groupPath: URL = Self.groupFilePath\n    ) throws -> ExecUser {\n        let defaults = defaults ?? ExecUser(uid: 0, gid: 0, sgids: [], home: \"/\", shell: \"\")\n\n        var user = ExecUser(\n            uid: defaults.uid,\n            gid: defaults.gid,\n            sgids: defaults.sgids,\n            home: defaults.home,\n            shell: defaults.shell\n        )\n\n        let parts = userString.split(\n            separator: \":\",\n            maxSplits: 1,\n            omittingEmptySubsequences: false\n        )\n        let userArg = parts.isEmpty ? \"\" : String(parts[0])\n        let groupArg = parts.count > 1 ? String(parts[1]) : \"\"\n\n        let uidArg = UInt32(userArg)\n        let notUID = uidArg == nil\n        let gidArg = UInt32(groupArg)\n        let notGID = gidArg == nil\n\n        let users: [User]\n        do {\n            users = try parsePasswd(passwdFile: passwdPath) { u in\n                if userArg.isEmpty {\n                    return u.uid == user.uid\n                }\n                if !notUID {\n                    return uidArg! == u.uid\n                }\n                return u.name == userArg\n            }\n        } catch Error.missingFile {\n            users = []\n        }\n\n        var matchedUserName = \"\"\n        if !users.isEmpty {\n            let matchedUser = users[0]\n            matchedUserName = matchedUser.name\n            user.uid = matchedUser.uid\n            user.gid = matchedUser.gid\n            user.home = matchedUser.home\n            user.shell = matchedUser.shell\n        } else if !userArg.isEmpty {\n            if notUID {\n                throw Error.noPasswdEntries\n            }\n\n            user.uid = uidArg!\n            if user.uid < minID || user.uid > maxID {\n                throw Error.range\n            }\n        }\n\n        if !groupArg.isEmpty || !matchedUserName.isEmpty {\n            let groups: [Group]\n            do {\n                groups = try parseGroup(groupFile: groupPath) { g in\n                    if groupArg.isEmpty {\n                        return g.users.contains(matchedUserName)\n                    }\n                    if !notGID {\n                        return gidArg! == g.gid\n                    }\n                    return g.name == groupArg\n                }\n            } catch Error.missingFile {\n                groups = []\n            }\n\n            if !groupArg.isEmpty {\n                if !groups.isEmpty {\n                    user.gid = groups[0].gid\n                } else {\n                    if notGID {\n                        throw Error.noGroupEntries\n                    }\n\n                    user.gid = gidArg!\n                    if user.gid < minID || user.gid > maxID {\n                        throw Error.range\n                    }\n                }\n            }\n            user.sgids = groups.map { $0.gid }\n        }\n        return user\n    }\n\n    public enum Error: Swift.Error {\n        case missingFile(String)\n        case range\n        case noPasswdEntries\n        case noGroupEntries\n        case parseError(String)\n    }\n}\n"
  },
  {
    "path": "Sources/Integration/ContainerTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ArgumentParser\nimport Containerization\nimport ContainerizationEXT4\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport ContainerizationOS\nimport Crypto\nimport Foundation\nimport Logging\nimport SystemPackage\n\nextension IntegrationSuite {\n    func testProcessTrue() async throws {\n        let id = \"test-process-true\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/true\"]\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n    }\n\n    func testProcessFalse() async throws {\n        let id = \"test-process-false\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/false\"]\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 1 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 1\")\n        }\n    }\n\n    final class DiscardingWriter: @unchecked Sendable, Writer {\n        var count: Int = 0\n\n        func write(_ data: Data) throws {\n            count += data.count\n        }\n\n        func close() throws {\n            return\n        }\n    }\n\n    final class BufferWriter: Writer {\n        // `data` isn't used concurrently.\n        nonisolated(unsafe) var data = Data()\n\n        func write(_ data: Data) throws {\n            guard data.count > 0 else {\n                return\n            }\n            self.data.append(data)\n        }\n\n        func close() throws {\n            return\n        }\n    }\n\n    final class StdinBuffer: ReaderStream {\n        let data: Data\n\n        init(data: Data) {\n            self.data = data\n        }\n\n        func stream() -> AsyncStream<Data> {\n            let (stream, cont) = AsyncStream<Data>.makeStream()\n            cont.yield(self.data)\n            cont.finish()\n            return stream\n        }\n    }\n\n    final class ChunkedStdinBuffer: ReaderStream {\n        let chunks: [Data]\n        let delayMs: Int\n\n        init(chunks: [Data], delayMs: Int = 0) {\n            self.chunks = chunks\n            self.delayMs = delayMs\n        }\n\n        func stream() -> AsyncStream<Data> {\n            let chunks = self.chunks\n            let delayMs = self.delayMs\n            return AsyncStream { cont in\n                Task {\n                    for chunk in chunks {\n                        if delayMs > 0 {\n                            try? await Task.sleep(for: .milliseconds(delayMs))\n                        }\n                        cont.yield(chunk)\n                    }\n                    cont.finish()\n                }\n            }\n        }\n    }\n\n    func testProcessEchoHi() async throws {\n        let id = \"test-process-echo-hi\"\n        let bs = try await bootstrap(id)\n\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/echo\", \"hi\"]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let status = try await container.wait()\n            try await container.stop()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"process status \\(status) != 1\")\n            }\n\n            guard String(data: buffer.data, encoding: .utf8) == \"hi\\n\" else {\n                throw IntegrationError.assert(\n                    msg: \"process should have returned on stdout 'hi' != '\\(String(data: buffer.data, encoding: .utf8)!)'\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testProcessNoExecutable() async throws {\n        let id = \"test-process-no-executable\"\n        let bs = try await bootstrap(id)\n\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"foobarbaz\"]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let _ = try await container.wait()\n            try await container.stop()\n\n            throw IntegrationError.assert(msg: \"process didn't throw 'no executable' error\")\n        } catch {\n            try? await container.stop()\n            guard let err = error as? ContainerizationError,\n                err.isCode(.internalError), err.description.contains(\"failed to find target executable\")\n            else {\n                throw error\n            }\n        }\n    }\n\n    func testMultipleConcurrentProcesses() async throws {\n        let id = \"test-concurrent-processes\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/sleep\", \"1000\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            try await withThrowingTaskGroup(of: Void.self) { group in\n                for i in 0...80 {\n                    let exec = try await container.exec(\"exec-\\(i)\") { config in\n                        config.arguments = [\"/bin/true\"]\n                    }\n\n                    group.addTask {\n                        try await exec.start()\n                        let status = try await exec.wait()\n                        if status.exitCode != 0 {\n                            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n                        }\n                        try await exec.delete()\n                    }\n                }\n\n                try await group.waitForAll()\n\n                try await container.stop()\n            }\n        } catch {\n            throw error\n        }\n    }\n\n    func testMultipleConcurrentProcessesOutputStress() async throws {\n        let id = \"test-concurrent-processes-output-stress\"\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/sleep\", \"1000\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let buffer = BufferWriter()\n            let exec = try await container.exec(\"expected-value\") { config in\n                config.arguments = [\n                    \"sh\",\n                    \"-c\",\n                    \"dd if=/dev/random of=/tmp/bytes bs=1M count=20 status=none ; sha256sum /tmp/bytes\",\n                ]\n                config.stdout = buffer\n            }\n\n            try await exec.start()\n            let status = try await exec.wait()\n            if status.exitCode != 0 {\n                throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n            }\n\n            let output = String(data: buffer.data, encoding: .utf8)!\n            let expected = String(output.split(separator: \" \").first!)\n            try await withThrowingTaskGroup(of: Void.self) { group in\n                for i in 0...80 {\n                    let idx = i\n                    group.addTask {\n                        let buffer = BufferWriter()\n                        let exec = try await container.exec(\"exec-\\(idx)\") { config in\n                            config.arguments = [\"cat\", \"/tmp/bytes\"]\n                            config.stdout = buffer\n                        }\n                        try await exec.start()\n\n                        let status = try await exec.wait()\n                        if status.exitCode != 0 {\n                            throw IntegrationError.assert(msg: \"process \\(idx) status \\(status) != 0\")\n                        }\n\n                        var hasher = SHA256()\n                        hasher.update(data: buffer.data)\n                        let hash = hasher.finalize().digestString.trimmingDigestPrefix\n                        guard hash == expected else {\n                            throw IntegrationError.assert(\n                                msg: \"process \\(idx) output \\(hash) != expected \\(expected)\")\n                        }\n                        try await exec.delete()\n                    }\n                }\n\n                try await group.waitForAll()\n            }\n            try await exec.delete()\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        }\n    }\n\n    func testProcessUser() async throws {\n        let id = \"test-process-user\"\n\n        let bs = try await bootstrap(id)\n        var buffer = BufferWriter()\n        var container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/usr/bin/id\"]\n            config.process.user = .init(uid: 1, gid: 1, additionalGids: [1])\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        var status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        var expected = \"uid=1(bin) gid=1(bin) groups=1(bin)\"\n        guard String(data: buffer.data, encoding: .utf8) == \"\\(expected)\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"process should have returned on stdout '\\(expected)' != '\\(String(data: buffer.data, encoding: .utf8)!)'\")\n        }\n\n        buffer = BufferWriter()\n        container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/usr/bin/id\"]\n            // Try some uid that doesn't exist. This is supported.\n            config.process.user = .init(uid: 40000, gid: 40000)\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        expected = \"uid=40000 gid=40000 groups=40000\"\n        guard String(data: buffer.data, encoding: .utf8) == \"\\(expected)\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"process should have returned on stdout '\\(expected)' != '\\(String(data: buffer.data, encoding: .utf8)!)'\")\n        }\n\n        buffer = BufferWriter()\n        container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/usr/bin/id\"]\n            // Try some uid that doesn't exist. This is supported.\n            config.process.user = .init(username: \"40000:40000\")\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        expected = \"uid=40000 gid=40000 groups=40000\"\n        guard String(data: buffer.data, encoding: .utf8) == \"\\(expected)\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"process should have returned on stdout '\\(expected)' != '\\(String(data: buffer.data, encoding: .utf8)!)'\")\n        }\n\n        buffer = BufferWriter()\n        container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/usr/bin/id\"]\n            // Now for our final trick, try and run a username that doesn't exist.\n            config.process.user = .init(username: \"thisdoesntexist\")\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        do {\n            try await container.start()\n        } catch {\n            return\n        }\n        throw IntegrationError.assert(msg: \"container start should have failed\")\n    }\n\n    // Ensure if we ask for a terminal we set TERM.\n    func testProcessTtyEnvvar() async throws {\n        let id = \"test-process-tty-envvar\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"env\"]\n            config.process.terminal = true\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard let str = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(\n                msg: \"failed to convert standard output to a UTF8 string\")\n        }\n\n        let homeEnvvar = \"TERM=xterm\"\n        guard str.contains(homeEnvvar) else {\n            throw IntegrationError.assert(\n                msg: \"process should have TERM environment variable defined\")\n        }\n    }\n\n    // Make sure we set HOME by default if we can find it in /etc/passwd in the guest.\n    func testProcessHomeEnvvar() async throws {\n        let id = \"test-process-home-envvar\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"env\"]\n            config.process.user = .init(uid: 0, gid: 0)\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard let str = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(\n                msg: \"failed to convert standard output to a UTF8 string\")\n        }\n\n        let homeEnvvar = \"HOME=/root\"\n        guard str.contains(homeEnvvar) else {\n            throw IntegrationError.assert(\n                msg: \"process should have HOME environment variable defined\")\n        }\n    }\n\n    func testProcessCustomHomeEnvvar() async throws {\n        let id = \"test-process-custom-home-envvar\"\n\n        let bs = try await bootstrap(id)\n        let customHomeEnvvar = \"HOME=/tmp/custom/home\"\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sh\", \"-c\", \"echo HOME=$HOME\"]\n            config.process.environmentVariables.append(customHomeEnvvar)\n            config.process.user = .init(uid: 0, gid: 0)\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        guard output.contains(customHomeEnvvar) else {\n            throw IntegrationError.assert(msg: \"process should have preserved custom HOME environment variable, expected \\(customHomeEnvvar), got: \\(output)\")\n        }\n    }\n\n    func testHostname() async throws {\n        let id = \"test-container-hostname\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/hostname\"]\n            config.hostname = \"foo-bar\"\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n        let expected = \"foo-bar\"\n\n        guard String(data: buffer.data, encoding: .utf8) == \"\\(expected)\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"process should have returned on stdout '\\(expected)' != '\\(String(data: buffer.data, encoding: .utf8)!)'\")\n        }\n    }\n\n    func testHostnameDefaultsToContainerID() async throws {\n        let id = \"test-container-hostname-default\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/hostname\"]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard String(data: buffer.data, encoding: .utf8) == \"\\(id)\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"hostname should default to container id '\\(id)', got '\\(String(data: buffer.data, encoding: .utf8)!)'\")\n        }\n    }\n\n    func testHostsFile() async throws {\n        let id = \"test-container-hosts-file\"\n\n        let bs = try await bootstrap(id)\n        let entry = Hosts.Entry.localHostIPV4(comment: \"Testaroo\")\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"cat\", \"/etc/hosts\"]\n            config.hosts = Hosts(entries: [entry])\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        let expected = entry.rendered\n        guard String(data: buffer.data, encoding: .utf8) == \"\\(expected)\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"process should have returned on stdout '\\(expected)' != '\\(String(data: buffer.data, encoding: .utf8)!)'\")\n        }\n    }\n\n    func testProcessStdin() async throws {\n        let id = \"test-container-stdin\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"cat\"]\n            config.process.stdin = StdinBuffer(data: \"Hello from test\".data(using: .utf8)!)\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n        let expected = \"Hello from test\"\n\n        guard String(data: buffer.data, encoding: .utf8) == \"\\(expected)\" else {\n            throw IntegrationError.assert(\n                msg: \"process should have returned on stdout '\\(expected)' != '\\(String(data: buffer.data, encoding: .utf8)!)'\")\n        }\n    }\n\n    func testMounts() async throws {\n        let id = \"test-cat-mount\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            let directory = try createMountDirectory()\n            config.process.arguments = [\"/bin/cat\", \"/mnt/hi.txt\"]\n            config.mounts.append(.share(source: directory.path, destination: \"/mnt\"))\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        let value = String(data: buffer.data, encoding: .utf8)\n        guard value == \"hello\" else {\n            throw IntegrationError.assert(\n                msg: \"process should have returned from file 'hello' != '\\(String(data: buffer.data, encoding: .utf8)!)\")\n\n        }\n    }\n\n    func testNestedVirtualizationEnabled() async throws {\n        let id = \"test-nested-virt\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/true\"]\n            config.virtualization = true\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n        } catch {\n            if let err = error as? ContainerizationError {\n                if err.code == .unsupported {\n                    throw SkipTest(reason: err.message)\n                }\n            }\n        }\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n    }\n\n    func testContainerManagerCreate() async throws {\n        let id = \"test-container-manager\"\n\n        let bs = try await bootstrap(id)\n\n        var manager = try ContainerManager(vmm: bs.vmm)\n        defer {\n            try? manager.delete(id)\n        }\n\n        let buffer = BufferWriter()\n        let container = try await manager.create(\n            id,\n            image: bs.image,\n            rootfs: bs.rootfs\n        ) { config in\n            config.process.arguments = [\"/bin/echo\", \"ContainerManager test\"]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        let output = String(data: buffer.data, encoding: .utf8)\n        guard output == \"ContainerManager test\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"process should have returned 'ContainerManager test' != '\\(output ?? \"nil\")'\")\n        }\n    }\n\n    func testContainerStopIdempotency() async throws {\n        let id = \"test-container-stop-idempotency\"\n\n        let bs = try await bootstrap(id)\n\n        var manager = try ContainerManager(vmm: bs.vmm)\n        defer {\n            try? manager.delete(id)\n        }\n\n        let buffer = BufferWriter()\n        let container = try await manager.create(\n            id,\n            image: bs.image,\n            rootfs: bs.rootfs\n        ) { config in\n            config.process.arguments = [\"/bin/echo\", \"please stop me\"]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        try await container.stop()\n        try await container.stop()\n\n        let output = String(data: buffer.data, encoding: .utf8)\n        guard output == \"please stop me\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"process should have returned 'ContainerManager test' != '\\(output ?? \"nil\")'\")\n        }\n    }\n\n    func testContainerReuse() async throws {\n        let id = \"test-container-reuse\"\n\n        let bs = try await bootstrap(id)\n\n        var manager = try ContainerManager(vmm: bs.vmm)\n        defer {\n            try? manager.delete(id)\n        }\n\n        let buffer = BufferWriter()\n        let container = try await manager.create(\n            id,\n            image: bs.image,\n            rootfs: bs.rootfs\n        ) { config in\n            config.process.arguments = [\"/bin/echo\", \"ContainerManager test\"]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        var status = try await container.wait()\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n        try await container.stop()\n\n        try await container.create()\n        try await container.start()\n\n        // Wait for completion.. again.\n        status = try await container.wait()\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        let output = String(data: buffer.data, encoding: .utf8)\n        let expected = \"ContainerManager test\\nContainerManager test\\n\"\n        guard output == expected else {\n            throw IntegrationError.assert(\n                msg: \"process should have returned '\\(expected)' != '\\(output ?? \"nil\")'\")\n        }\n    }\n\n    func testContainerDevConsole() async throws {\n        let id = \"test-container-devconsole\"\n\n        let bs = try await bootstrap(id)\n\n        var manager = try ContainerManager(vmm: bs.vmm)\n        defer {\n            try? manager.delete(id)\n        }\n\n        let buffer = BufferWriter()\n        let container = try await manager.create(\n            id,\n            image: bs.image,\n            rootfs: bs.rootfs\n        ) { config in\n            // We mount devtmpfs by default, and while this includes creating\n            // /dev/console typically that'll be pointing to /dev/hvc0 (the\n            // virtio serial console). This is just a character device, so a trivial\n            // way to check that our bind mounted console setup worked is by just\n            // parsing `mount`'s output and looking for /dev/console as it wouldn't\n            // be there normally without our dance.\n            config.process.arguments = [\"mount\"]\n            config.process.terminal = true\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard let str = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(\n                msg: \"failed to convert standard output to a UTF8 string\")\n        }\n\n        let devConsole = \"/dev/console\"\n        guard str.contains(devConsole) else {\n            throw IntegrationError.assert(\n                msg: \"process should have \\(devConsole) in `mount` output\")\n        }\n    }\n\n    func testContainerStatistics() async throws {\n        let id = \"test-container-statistics\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"infinity\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let stats = try await container.statistics()\n\n            guard stats.id == id else {\n                throw IntegrationError.assert(msg: \"stats container ID '\\(stats.id)' != '\\(id)'\")\n            }\n\n            guard let process = stats.process, process.current > 0 else {\n                throw IntegrationError.assert(msg: \"process count should be > 0, got \\(stats.process?.current ?? 0)\")\n            }\n\n            guard let memory = stats.memory, memory.usageBytes > 0 else {\n                throw IntegrationError.assert(msg: \"memory usage should be > 0, got \\(stats.memory?.usageBytes ?? 0)\")\n            }\n\n            guard let cpu = stats.cpu, cpu.usageUsec > 0 else {\n                throw IntegrationError.assert(msg: \"CPU usage should be > 0, got \\(stats.cpu?.usageUsec ?? 0)\")\n            }\n\n            print(\"Container statistics:\")\n            print(\"  Processes: \\(process.current)\")\n            print(\"  Memory: \\(memory.usageBytes) bytes\")\n            print(\"  CPU: \\(cpu.usageUsec) usec\")\n            print(\"  Networks: \\(stats.networks?.count ?? 0) interfaces\")\n\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testCgroupLimits() async throws {\n        let id = \"test-cgroup-limits\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"infinity\"]\n            config.cpus = 2\n            config.memoryInBytes = 512.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Start an exec with sleep infinity\n            let sleepExec = try await container.exec(\"sleep-exec\") { config in\n                config.arguments = [\"sleep\", \"infinity\"]\n            }\n            try await sleepExec.start()\n\n            // Verify we have 3 PIDs in cgroup.procs: init, exec sleep, and cat itself\n            let procsBuffer = BufferWriter()\n            let procsExec = try await container.exec(\"check-procs\") { config in\n                config.arguments = [\"cat\", \"/sys/fs/cgroup/cgroup.procs\"]\n                config.stdout = procsBuffer\n            }\n            try await procsExec.start()\n            var status = try await procsExec.wait()\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"check-procs status \\(status) != 0\")\n            }\n            try await procsExec.delete()\n\n            guard let procsContent = String(data: procsBuffer.data, encoding: .utf8) else {\n                throw IntegrationError.assert(msg: \"failed to parse cgroup.procs\")\n            }\n            let pids = procsContent.split(separator: \"\\n\").filter { !$0.isEmpty }\n            guard pids.count == 3 else {\n                throw IntegrationError.assert(msg: \"expected 3 PIDs in cgroup.procs, got \\(pids.count): \\(procsContent)\")\n            }\n\n            // Verify memory limit\n            let memoryBuffer = BufferWriter()\n            let memoryExec = try await container.exec(\"check-memory\") { config in\n                config.arguments = [\"cat\", \"/sys/fs/cgroup/memory.max\"]\n                config.stdout = memoryBuffer\n            }\n            try await memoryExec.start()\n            status = try await memoryExec.wait()\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"check-memory status \\(status) != 0\")\n            }\n            try await memoryExec.delete()\n\n            guard let memoryLimit = String(data: memoryBuffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {\n                throw IntegrationError.assert(msg: \"failed to parse memory.max\")\n            }\n            let expectedMemory = \"\\(512.mib())\"\n            guard memoryLimit == expectedMemory else {\n                throw IntegrationError.assert(msg: \"memory.max \\(memoryLimit) != expected \\(expectedMemory)\")\n            }\n\n            // Verify CPU limit\n            let cpuBuffer = BufferWriter()\n            let cpuExec = try await container.exec(\"check-cpu\") { config in\n                config.arguments = [\"cat\", \"/sys/fs/cgroup/cpu.max\"]\n                config.stdout = cpuBuffer\n            }\n            try await cpuExec.start()\n            status = try await cpuExec.wait()\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"check-cpu status \\(status) != 0\")\n            }\n            try await cpuExec.delete()\n\n            guard let cpuLimit = String(data: cpuBuffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {\n                throw IntegrationError.assert(msg: \"failed to parse cpu.max\")\n            }\n            let expectedCpu = \"200000 100000\"  // 2 CPUs: quota=200000, period=100000\n            guard cpuLimit == expectedCpu else {\n                throw IntegrationError.assert(msg: \"cpu.max '\\(cpuLimit)' != expected '\\(expectedCpu)'\")\n            }\n\n            try await sleepExec.delete()\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testMemoryEventsOOMKill() async throws {\n        let id = \"test-memory-events-oom-kill\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"infinity\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Run a process that will exceed the memory limit and get OOM-killed\n            let exec = try await container.exec(\"oom-trigger\") { config in\n                // First set a 2MB memory limit on the container's cgroup, then allocate more\n                config.arguments = [\n                    \"sh\",\n                    \"-c\",\n                    \"echo 2097152 > /sys/fs/cgroup/memory.max && dd if=/dev/zero of=/dev/null bs=100M\",\n                ]\n            }\n\n            try await exec.start()\n            let status = try await exec.wait()\n            if status.exitCode == 0 {\n                throw IntegrationError.assert(msg: \"expected exit code > 0\")\n            }\n            try await exec.delete()\n\n            let stats = try await container.statistics(categories: .memoryEvents)\n\n            guard let events = stats.memoryEvents else {\n                throw IntegrationError.assert(msg: \"expected memoryEvents to be present\")\n            }\n\n            print(\"Memory events for container \\(id):\")\n            print(\"  low: \\(events.low)\")\n            print(\"  high: \\(events.high)\")\n            print(\"  max: \\(events.max)\")\n            print(\"  oom: \\(events.oom)\")\n            print(\"  oomKill: \\(events.oomKill)\")\n\n            guard events.oomKill > 0 else {\n                throw IntegrationError.assert(msg: \"expected oomKill > 0, got \\(events.oomKill)\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testNoSerialConsole() async throws {\n        let id = \"test-no-serial-console\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/true\"]\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n    }\n\n    func testUnixSocketIntoGuest() async throws {\n        let id = \"test-unixsocket-into-guest\"\n\n        let bs = try await bootstrap(id)\n\n        let hostSocketPath = try createHostUnixSocket()\n\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.sockets = [\n                UnixSocketConfiguration(\n                    source: URL(filePath: hostSocketPath),\n                    destination: URL(filePath: \"/tmp/test.sock\"),\n                    direction: .into\n                )\n            ]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Execute ls -l to check the socket exists and is indeed a socket\n            let lsExec = try await container.exec(\"ls-socket\") { config in\n                config.arguments = [\"ls\", \"-l\", \"/tmp/test.sock\"]\n                config.stdout = buffer\n            }\n\n            try await lsExec.start()\n            let status = try await lsExec.wait()\n            try await lsExec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"ls command failed with status \\(status)\")\n            }\n\n            guard let output = String(data: buffer.data, encoding: .utf8) else {\n                throw IntegrationError.assert(msg: \"failed to convert ls output to UTF8\")\n            }\n\n            // Socket files in ls -l output start with 's'\n            guard output.hasPrefix(\"s\") else {\n                throw IntegrationError.assert(\n                    msg: \"expected socket file (starting with 's'), got: \\(output)\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testNonClosureConstructor() async throws {\n        let id = \"test-container-non-closure-constructor\"\n\n        let bs = try await bootstrap(id)\n        let config = LinuxContainer.Configuration(\n            process: LinuxProcessConfiguration(arguments: [\"/bin/true\"])\n        )\n        let container = try LinuxContainer(\n            id,\n            rootfs: bs.rootfs,\n            vmm: bs.vmm,\n            configuration: config\n        )\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n    }\n\n    private func createHostUnixSocket() throws -> String {\n        let dir = FileManager.default.uniqueTemporaryDirectory(create: true)\n        let socketPath = dir.appendingPathComponent(\"test.sock\").path\n\n        let socket = try Socket(type: UnixType(path: socketPath))\n        try socket.listen()\n\n        return socketPath\n    }\n\n    private func createMountDirectory() throws -> URL {\n        let dir = FileManager.default.uniqueTemporaryDirectory(create: true)\n        try \"hello\".write(to: dir.appendingPathComponent(\"hi.txt\"), atomically: true, encoding: .utf8)\n        return dir\n    }\n\n    func testUnixSocketIntoGuestSymlink() async throws {\n        let id = \"test-unixsocket-into-guest-symlink\"\n\n        let bs = try await bootstrap(id)\n\n        let hostSocketPath = try createHostUnixSocket()\n\n        let buffer = BufferWriter()\n        // Use /var/run/test.sock. Alpine has /var/run -> /run symlink\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.sockets = [\n                UnixSocketConfiguration(\n                    source: URL(filePath: hostSocketPath),\n                    destination: URL(filePath: \"/var/run/test.sock\"),\n                    direction: .into\n                )\n            ]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let lsExec = try await container.exec(\"ls-socket\") { config in\n                config.arguments = [\"ls\", \"-l\", \"/var/run/test.sock\"]\n                config.stdout = buffer\n            }\n\n            try await lsExec.start()\n            let status = try await lsExec.wait()\n            try await lsExec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"ls command failed with status \\(status)\")\n            }\n\n            guard let output = String(data: buffer.data, encoding: .utf8) else {\n                throw IntegrationError.assert(msg: \"failed to convert ls output to UTF8\")\n            }\n\n            // Socket files in ls -l output start with 's'\n            guard output.hasPrefix(\"s\") else {\n                throw IntegrationError.assert(\n                    msg: \"expected socket file (starting with 's'), got: \\(output)\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testBootLogFileHandle() async throws {\n        let id = \"test-bootlog-filehandle\"\n\n        let bs = try await bootstrap(id)\n\n        // Create a pipe to capture boot log data\n        let pipe = Pipe()\n        let bootLog = BootLog.fileHandle(pipe.fileHandleForWriting)\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/echo\", \"test complete\"]\n            config.bootLog = bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let status = try await container.wait()\n            try await container.stop()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n            }\n\n            try pipe.fileHandleForWriting.close()\n            let bootLogData = try pipe.fileHandleForReading.readToEnd()\n            guard let bootLogData = bootLogData, bootLogData.count > 0 else {\n                throw IntegrationError.assert(\n                    msg: \"expected to receive boot log data from pipe, but got no data\")\n            }\n\n            guard let bootLogString = String(data: bootLogData, encoding: .utf8) else {\n                throw IntegrationError.assert(\n                    msg: \"failed to convert boot log data to UTF8 string\")\n            }\n\n            guard bootLogString.count > 100 else {\n                throw IntegrationError.assert(\n                    msg: \"boot log output smaller than expected: got \\(bootLogString.count)\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testLargeStdioOutput() async throws {\n        let id = \"test-large-stdout-stderr-output\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/sleep\", \"1000\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let stdoutBuffer = DiscardingWriter()\n            let stderrBuffer = DiscardingWriter()\n\n            let exec = try await container.exec(\"large-output\") { config in\n                config.arguments = [\n                    \"sh\",\n                    \"-c\",\n                    \"\"\"\n                    dd if=/dev/zero bs=1M count=250 status=none && \\\n                    dd if=/dev/zero bs=1M count=250 status=none >&2\n                    \"\"\",\n                ]\n                config.stdout = stdoutBuffer\n                config.stderr = stderrBuffer\n            }\n\n            let started = CFAbsoluteTimeGetCurrent()\n\n            try await exec.start()\n            let status = try await exec.wait()\n\n            let lasted = CFAbsoluteTimeGetCurrent() - started\n            print(\"Test \\(id) finished process ingesting stdio in \\(lasted)\")\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"exec process status \\(status) != 0\")\n            }\n\n            try await exec.delete()\n\n            let expectedSize = 250 * 1024 * 1024\n            guard stdoutBuffer.count == expectedSize else {\n                throw IntegrationError.assert(\n                    msg: \"stdout size \\(stdoutBuffer.count) != expected \\(expectedSize)\")\n            }\n\n            guard stderrBuffer.count == expectedSize else {\n                throw IntegrationError.assert(\n                    msg: \"stderr size \\(stderrBuffer.count) != expected \\(expectedSize)\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testProcessDeleteIdempotency() async throws {\n        let id = \"test-process-delete-idempotency\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/sleep\", \"1000\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Create an exec process\n            let exec = try await container.exec(\"test-exec\") { config in\n                config.arguments = [\"/bin/true\"]\n            }\n\n            try await exec.start()\n            let status = try await exec.wait()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"exec process status \\(status) != 0\")\n            }\n\n            // Call delete twice to verify idempotency\n            try await exec.delete()\n            try await exec.delete()  // Should be a no-op\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testMultipleExecsWithoutDelete() async throws {\n        let id = \"test-multiple-execs-without-delete\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/sleep\", \"1000\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Create 3 exec processes without deleting them\n            let exec1 = try await container.exec(\"exec-1\") { config in\n                config.arguments = [\"/bin/true\"]\n            }\n            try await exec1.start()\n            let status1 = try await exec1.wait()\n            guard status1.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"exec1 process status \\(status1) != 0\")\n            }\n\n            let exec2 = try await container.exec(\"exec-2\") { config in\n                config.arguments = [\"/bin/true\"]\n            }\n            try await exec2.start()\n            let status2 = try await exec2.wait()\n            guard status2.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"exec2 process status \\(status2) != 0\")\n            }\n\n            let exec3 = try await container.exec(\"exec-3\") { config in\n                config.arguments = [\"/bin/true\"]\n            }\n            try await exec3.start()\n            let status3 = try await exec3.wait()\n            guard status3.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"exec3 process status \\(status3) != 0\")\n            }\n\n            // Stop should handle cleanup of all exec processes gracefully\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testNonExistentBinary() async throws {\n        let id = \"test-non-existent-binary\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"foo-bar-baz\"]\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        do {\n            try await container.start()\n        } catch {\n            return\n        }\n        try await container.stop()\n        throw IntegrationError.assert(msg: \"container start should have failed\")\n    }\n\n    // MARK: - Capability Tests\n\n    func testCapabilitiesSysAdmin() async throws {\n        let id = \"test-capabilities-sysadmin\"\n\n        let bs = try await bootstrap(id)\n\n        // First test: without CAP_SYS_ADMIN (should be denied)\n        let bufferDenied = BufferWriter()\n        let containerWithoutSysAdmin = try LinuxContainer(\"\\(id)-denied\", rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.capabilities = LinuxCapabilities()\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"mount -t tmpfs tmpfs /tmp || echo 'mount failed as expected'\"]\n            config.process.stdout = bufferDenied\n            config.bootLog = bs.bootLog\n        }\n\n        try await containerWithoutSysAdmin.create()\n        try await containerWithoutSysAdmin.start()\n\n        var status = try await containerWithoutSysAdmin.wait()\n        try await containerWithoutSysAdmin.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container should have run successfully, got exit code \\(status.exitCode)\")\n        }\n\n        guard let outputDenied = String(data: bufferDenied.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        guard outputDenied.contains(\"mount failed as expected\") else {\n            throw IntegrationError.assert(msg: \"expected mount failure message, got: \\(outputDenied)\")\n        }\n\n        // Second test: with CAP_SYS_ADMIN (should succeed)\n        let containerWithSysAdmin = try LinuxContainer(\"\\(id)-allowed\", rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.capabilities = LinuxCapabilities(capabilities: [.sysAdmin])\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"mount -t tmpfs tmpfs /tmp\"]\n            config.bootLog = bs.bootLog\n        }\n\n        try await containerWithSysAdmin.create()\n        try await containerWithSysAdmin.start()\n\n        status = try await containerWithSysAdmin.wait()\n        try await containerWithSysAdmin.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container with CAP_SYS_ADMIN should mount successfully, got exit code \\(status.exitCode)\")\n        }\n    }\n\n    func testCapabilitiesNetAdmin() async throws {\n        let id = \"test-capabilities-netadmin\"\n\n        let bs = try await bootstrap(id)\n\n        // First test: without CAP_NET_ADMIN (should be denied)\n        let bufferDenied = BufferWriter()\n        let containerWithoutNetAdmin = try LinuxContainer(\"\\(id)-denied\", rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.capabilities = LinuxCapabilities()\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"ip link set lo down 2>/dev/null || echo 'network operation denied as expected'\"]\n            config.process.stdout = bufferDenied\n            config.bootLog = bs.bootLog\n        }\n\n        try await containerWithoutNetAdmin.create()\n        try await containerWithoutNetAdmin.start()\n\n        var status = try await containerWithoutNetAdmin.wait()\n        try await containerWithoutNetAdmin.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container should handle network denial gracefully, got exit code \\(status.exitCode)\")\n        }\n\n        guard let outputDenied = String(data: bufferDenied.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        guard outputDenied.contains(\"network operation denied as expected\") else {\n            throw IntegrationError.assert(msg: \"expected network denial message, got: \\(outputDenied)\")\n        }\n\n        // Second test: with CAP_NET_ADMIN (should succeed)\n        let bufferAllowed = BufferWriter()\n        let containerWithNetAdmin = try LinuxContainer(\"\\(id)-allowed\", rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.capabilities = LinuxCapabilities(capabilities: [.netAdmin])\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"ip link set lo down && ip link set lo up\"]\n            config.process.stdout = bufferAllowed\n            config.bootLog = bs.bootLog\n        }\n\n        try await containerWithNetAdmin.create()\n        try await containerWithNetAdmin.start()\n\n        status = try await containerWithNetAdmin.wait()\n        try await containerWithNetAdmin.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container with CAP_NET_ADMIN should perform network operations, got exit code \\(status.exitCode)\")\n        }\n    }\n\n    func testCapabilitiesOCIDefault() async throws {\n        let id = \"test-capabilities-OCI-default\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            // Use default capability set\n            config.process.capabilities = .defaultOCICapabilities\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"echo 'Running with OCI default capabilities'\"]\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container with OCI default capabilities should run, got exit code \\(status.exitCode)\")\n        }\n    }\n\n    func testCapabilitiesAllCapabilities() async throws {\n        let id = \"test-capabilities-all\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.capabilities = .allCapabilities\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"mount -t tmpfs tmpfs /tmp && ip link set lo down\"]\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container with all capabilities should perform all operations, got exit code \\(status.exitCode)\")\n        }\n    }\n\n    func testCapabilitiesFileOwnership() async throws {\n        let id = \"test-capabilities-chown\"\n\n        let bs = try await bootstrap(id)\n\n        // First test: without CAP_CHOWN\n        let bufferDenied = BufferWriter()\n        let containerWithoutChown = try LinuxContainer(\"\\(id)-denied\", rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.capabilities = LinuxCapabilities()\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"touch /tmp/testfile && chown 1000:1000 /tmp/testfile 2>/dev/null || echo 'chown denied as expected'\"]\n            config.process.stdout = bufferDenied\n            config.bootLog = bs.bootLog\n        }\n\n        try await containerWithoutChown.create()\n        try await containerWithoutChown.start()\n\n        var status = try await containerWithoutChown.wait()\n        try await containerWithoutChown.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container should handle chown denial gracefully, got exit code \\(status.exitCode)\")\n        }\n\n        guard let outputDenied = String(data: bufferDenied.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        guard outputDenied.contains(\"chown denied as expected\") else {\n            throw IntegrationError.assert(msg: \"expected chown denial message, got: \\(outputDenied)\")\n        }\n\n        // Second test: with CAP_CHOWN\n        let bufferAllowed = BufferWriter()\n        let containerWithChown = try LinuxContainer(\"\\(id)-allowed\", rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.capabilities = LinuxCapabilities(capabilities: [.chown])\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"touch /tmp/testfile && chown 1000:1000 /tmp/testfile\"]\n            config.process.stdout = bufferAllowed\n            config.bootLog = bs.bootLog\n        }\n\n        try await containerWithChown.create()\n        try await containerWithChown.start()\n\n        status = try await containerWithChown.wait()\n        try await containerWithChown.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container with CAP_CHOWN should succeed, got exit code \\(status.exitCode)\")\n        }\n    }\n\n    func testCopyIn() async throws {\n        let id = \"test-copy-in\"\n\n        let bs = try await bootstrap(id)\n\n        // Create a temp file on the host with known content\n        let testContent = \"Hello from the host! This is a copyIn test.\"\n        let hostFile = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"test-input.txt\")\n        try testContent.write(to: hostFile, atomically: true, encoding: .utf8)\n\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Copy the file into the container\n            try await container.copyIn(\n                from: hostFile,\n                to: URL(filePath: \"/tmp/copied-file.txt\")\n            )\n\n            // Verify the file exists and has correct content\n            let exec = try await container.exec(\"verify-copy\") { config in\n                config.arguments = [\"cat\", \"/tmp/copied-file.txt\"]\n                config.stdout = buffer\n            }\n\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"cat command failed with status \\(status)\")\n            }\n\n            guard let output = String(data: buffer.data, encoding: .utf8) else {\n                throw IntegrationError.assert(msg: \"failed to convert output to UTF8\")\n            }\n\n            guard output == testContent else {\n                throw IntegrationError.assert(\n                    msg: \"copied file content mismatch: expected '\\(testContent)', got '\\(output)'\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testCopyOut() async throws {\n        let id = \"test-copy-out\"\n\n        let bs = try await bootstrap(id)\n\n        let testContent = \"Hello from the guest! This is a copyOut test.\"\n        let hostDestination = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"test-output.txt\")\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Create a file inside the container\n            let exec = try await container.exec(\"create-file\") { config in\n                config.arguments = [\"sh\", \"-c\", \"echo -n '\\(testContent)' > /tmp/guest-file.txt\"]\n            }\n\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"failed to create file in guest, status \\(status)\")\n            }\n\n            // Copy the file out of the container\n            try await container.copyOut(\n                from: URL(filePath: \"/tmp/guest-file.txt\"),\n                to: hostDestination\n            )\n\n            // Verify the file was copied correctly\n            let copiedContent = try String(contentsOf: hostDestination, encoding: .utf8)\n\n            guard copiedContent == testContent else {\n                throw IntegrationError.assert(\n                    msg: \"copied file content mismatch: expected '\\(testContent)', got '\\(copiedContent)'\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testCopyLargeFile() async throws {\n        let id = \"test-copy-large-file\"\n\n        let bs = try await bootstrap(id)\n\n        // Create a 10MB file on the host with a repeating pattern\n        let fileSize = 10 * 1024 * 1024\n        let hostFile = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"large-file.bin\")\n\n        // Generate data with a repeating pattern\n        let pattern = Data(\"ContainerizationCopyTest\".utf8)\n        var testData = Data(capacity: fileSize)\n        while testData.count < fileSize {\n            testData.append(pattern)\n        }\n        testData = testData.prefix(fileSize)\n        try testData.write(to: hostFile)\n\n        let hostDestination = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"large-file-out.bin\")\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Copy large file into the container\n            try await container.copyIn(\n                from: hostFile,\n                to: URL(filePath: \"/tmp/large-file.bin\")\n            )\n\n            // Copy it back out\n            try await container.copyOut(\n                from: URL(filePath: \"/tmp/large-file.bin\"),\n                to: hostDestination\n            )\n\n            // Verify the content matches\n            let copiedData = try Data(contentsOf: hostDestination)\n\n            guard copiedData.count == testData.count else {\n                throw IntegrationError.assert(\n                    msg: \"file size mismatch: expected \\(testData.count), got \\(copiedData.count)\")\n            }\n\n            guard copiedData == testData else {\n                throw IntegrationError.assert(msg: \"file content mismatch after round-trip copy\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testCopyInDirectory() async throws {\n        let id = \"test-copy-in-dir\"\n\n        let bs = try await bootstrap(id)\n\n        // Create a temp directory with files, a subdirectory, and a symlink.\n        let hostDir = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"test-dir\")\n        try FileManager.default.createDirectory(at: hostDir, withIntermediateDirectories: true)\n        try \"file1 content\".write(to: hostDir.appendingPathComponent(\"file1.txt\"), atomically: true, encoding: .utf8)\n\n        let subDir = hostDir.appendingPathComponent(\"subdir\")\n        try FileManager.default.createDirectory(at: subDir, withIntermediateDirectories: true)\n        try \"file2 content\".write(to: subDir.appendingPathComponent(\"file2.txt\"), atomically: true, encoding: .utf8)\n\n        try FileManager.default.createSymbolicLink(\n            at: hostDir.appendingPathComponent(\"link.txt\"),\n            withDestinationURL: hostDir.appendingPathComponent(\"file1.txt\")\n        )\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Copy the directory into the container.\n            try await container.copyIn(\n                from: hostDir,\n                to: URL(filePath: \"/tmp/copied-dir\")\n            )\n\n            // Verify file1.txt exists with correct content.\n            let buffer1 = BufferWriter()\n            let exec1 = try await container.exec(\"verify-file1\") { config in\n                config.arguments = [\"cat\", \"/tmp/copied-dir/file1.txt\"]\n                config.stdout = buffer1\n            }\n            try await exec1.start()\n            let status1 = try await exec1.wait()\n            try await exec1.delete()\n\n            guard status1.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"cat file1.txt failed with status \\(status1)\")\n            }\n            guard String(data: buffer1.data, encoding: .utf8) == \"file1 content\" else {\n                throw IntegrationError.assert(msg: \"file1.txt content mismatch\")\n            }\n\n            // Verify subdir/file2.txt exists with correct content.\n            let buffer2 = BufferWriter()\n            let exec2 = try await container.exec(\"verify-file2\") { config in\n                config.arguments = [\"cat\", \"/tmp/copied-dir/subdir/file2.txt\"]\n                config.stdout = buffer2\n            }\n            try await exec2.start()\n            let status2 = try await exec2.wait()\n            try await exec2.delete()\n\n            guard status2.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"cat subdir/file2.txt failed with status \\(status2)\")\n            }\n            guard String(data: buffer2.data, encoding: .utf8) == \"file2 content\" else {\n                throw IntegrationError.assert(msg: \"subdir/file2.txt content mismatch\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testCopyOutDirectory() async throws {\n        let id = \"test-copy-out-dir\"\n\n        let bs = try await bootstrap(id)\n\n        let hostDestination = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"copied-out-dir\")\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Create a directory structure inside the container.\n            let exec = try await container.exec(\"create-dir\") { config in\n                config.arguments = [\n                    \"sh\", \"-c\",\n                    \"mkdir -p /tmp/guest-dir/subdir && echo -n 'guest file1' > /tmp/guest-dir/file1.txt && echo -n 'guest file2' > /tmp/guest-dir/subdir/file2.txt\",\n                ]\n            }\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"failed to create directory in guest, status \\(status)\")\n            }\n\n            // Copy the directory out of the container.\n            try await container.copyOut(\n                from: URL(filePath: \"/tmp/guest-dir\"),\n                to: hostDestination\n            )\n\n            // Verify file1.txt was copied correctly.\n            let file1Content = try String(contentsOf: hostDestination.appendingPathComponent(\"file1.txt\"), encoding: .utf8)\n            guard file1Content == \"guest file1\" else {\n                throw IntegrationError.assert(\n                    msg: \"file1.txt content mismatch: expected 'guest file1', got '\\(file1Content)'\")\n            }\n\n            // Verify subdir/file2.txt was copied correctly.\n            let file2Content = try String(\n                contentsOf: hostDestination.appendingPathComponent(\"subdir\").appendingPathComponent(\"file2.txt\"),\n                encoding: .utf8\n            )\n            guard file2Content == \"guest file2\" else {\n                throw IntegrationError.assert(\n                    msg: \"subdir/file2.txt content mismatch: expected 'guest file2', got '\\(file2Content)'\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testCopyEmptyFile() async throws {\n        let id = \"test-copy-empty-file\"\n\n        let bs = try await bootstrap(id)\n\n        let hostFile = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"empty.txt\")\n        try Data().write(to: hostFile)\n\n        let hostDestination = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"empty-out.txt\")\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Copy empty file in.\n            try await container.copyIn(\n                from: hostFile,\n                to: URL(filePath: \"/tmp/empty.txt\")\n            )\n\n            // Verify it exists and is empty in the guest.\n            let buffer = BufferWriter()\n            let exec = try await container.exec(\"verify-empty\") { config in\n                config.arguments = [\"stat\", \"-c\", \"%s\", \"/tmp/empty.txt\"]\n                config.stdout = buffer\n            }\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"stat failed with status \\(status)\")\n            }\n            let sizeStr = String(data: buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)\n            guard sizeStr == \"0\" else {\n                throw IntegrationError.assert(msg: \"empty file should have size 0, got '\\(sizeStr ?? \"nil\")'\")\n            }\n\n            // Copy it back out.\n            try await container.copyOut(\n                from: URL(filePath: \"/tmp/empty.txt\"),\n                to: hostDestination\n            )\n\n            let copiedData = try Data(contentsOf: hostDestination)\n            guard copiedData.isEmpty else {\n                throw IntegrationError.assert(msg: \"round-tripped empty file should be empty, got \\(copiedData.count) bytes\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testCopyEmptyDirectory() async throws {\n        let id = \"test-copy-empty-dir\"\n\n        let bs = try await bootstrap(id)\n\n        let hostDir = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"empty-dir\")\n        try FileManager.default.createDirectory(at: hostDir, withIntermediateDirectories: true)\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Copy empty directory in.\n            try await container.copyIn(\n                from: hostDir,\n                to: URL(filePath: \"/tmp/empty-dir\")\n            )\n\n            // Verify it exists and is a directory.\n            let buffer = BufferWriter()\n            let exec = try await container.exec(\"verify-empty-dir\") { config in\n                config.arguments = [\"sh\", \"-c\", \"test -d /tmp/empty-dir && ls -a /tmp/empty-dir | wc -l\"]\n                config.stdout = buffer\n            }\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"empty dir check failed with status \\(status)\")\n            }\n\n            // ls -a shows . and .. so count should be 2 for an empty dir.\n            let countStr = String(data: buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)\n            guard countStr == \"2\" else {\n                throw IntegrationError.assert(msg: \"empty dir should have 2 entries (. and ..), got '\\(countStr ?? \"nil\")'\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testCopyBinaryFile() async throws {\n        let id = \"test-copy-binary\"\n\n        let bs = try await bootstrap(id)\n\n        // Create a file with all 256 byte values to test binary safety.\n        let hostFile = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"binary.bin\")\n        var binaryData = Data(count: 256 * 64)\n        for i in 0..<binaryData.count {\n            binaryData[i] = UInt8(i % 256)\n        }\n        try binaryData.write(to: hostFile)\n\n        let hostDestination = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"binary-out.bin\")\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            try await container.copyIn(\n                from: hostFile,\n                to: URL(filePath: \"/tmp/binary.bin\")\n            )\n\n            try await container.copyOut(\n                from: URL(filePath: \"/tmp/binary.bin\"),\n                to: hostDestination\n            )\n\n            let copiedData = try Data(contentsOf: hostDestination)\n\n            guard copiedData.count == binaryData.count else {\n                throw IntegrationError.assert(\n                    msg: \"binary file size mismatch: expected \\(binaryData.count), got \\(copiedData.count)\")\n            }\n            guard copiedData == binaryData else {\n                throw IntegrationError.assert(msg: \"binary file content mismatch after round-trip\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testCopyMultipleFiles() async throws {\n        let id = \"test-copy-multiple\"\n\n        let bs = try await bootstrap(id)\n\n        let tmpDir = FileManager.default.uniqueTemporaryDirectory(create: true)\n        let files = (0..<5).map { i in\n            (\n                host: tmpDir.appendingPathComponent(\"file\\(i).txt\"),\n                guest: URL(filePath: \"/tmp/multi/file\\(i).txt\"),\n                content: \"Content of file \\(i) with some padding: \\(String(repeating: \"x\", count: i * 1000))\"\n            )\n        }\n\n        for file in files {\n            try file.content.write(to: file.host, atomically: true, encoding: .utf8)\n        }\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Copy all files in sequentially.\n            for file in files {\n                try await container.copyIn(\n                    from: file.host,\n                    to: file.guest,\n                    createParents: true\n                )\n            }\n\n            // Verify each file.\n            for (i, file) in files.enumerated() {\n                let buffer = BufferWriter()\n                let exec = try await container.exec(\"verify-\\(i)\") { config in\n                    config.arguments = [\"cat\", file.guest.path]\n                    config.stdout = buffer\n                }\n                try await exec.start()\n                let status = try await exec.wait()\n                try await exec.delete()\n\n                guard status.exitCode == 0 else {\n                    throw IntegrationError.assert(msg: \"cat file\\(i).txt failed with status \\(status)\")\n                }\n                let output = String(data: buffer.data, encoding: .utf8)\n                guard output == file.content else {\n                    throw IntegrationError.assert(msg: \"file\\(i).txt content mismatch\")\n                }\n            }\n\n            // Copy all files back out and verify.\n            for (i, file) in files.enumerated() {\n                let outPath = tmpDir.appendingPathComponent(\"out-file\\(i).txt\")\n                try await container.copyOut(\n                    from: file.guest,\n                    to: outPath\n                )\n                let copiedContent = try String(contentsOf: outPath, encoding: .utf8)\n                guard copiedContent == file.content else {\n                    throw IntegrationError.assert(msg: \"file\\(i).txt round-trip content mismatch\")\n                }\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testCopyDirectoryRoundTrip() async throws {\n        let id = \"test-copy-dir-rt\"\n\n        let bs = try await bootstrap(id)\n\n        // Create a directory tree with varied content: nested dirs, different sizes, binary data.\n        let hostDir = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"dir-rt\")\n        try FileManager.default.createDirectory(at: hostDir, withIntermediateDirectories: true)\n\n        // Top-level files.\n        try \"top level\".write(to: hostDir.appendingPathComponent(\"root.txt\"), atomically: true, encoding: .utf8)\n        try Data(repeating: 0xAB, count: 4096).write(to: hostDir.appendingPathComponent(\"binary.bin\"))\n\n        // Nested 3 levels deep.\n        let deep = hostDir.appendingPathComponent(\"a\").appendingPathComponent(\"b\").appendingPathComponent(\"c\")\n        try FileManager.default.createDirectory(at: deep, withIntermediateDirectories: true)\n        try \"deep file\".write(to: deep.appendingPathComponent(\"deep.txt\"), atomically: true, encoding: .utf8)\n\n        // Empty subdirectory.\n        let emptySubdir = hostDir.appendingPathComponent(\"empty-sub\")\n        try FileManager.default.createDirectory(at: emptySubdir, withIntermediateDirectories: true)\n\n        // File with special characters in content (not name).\n        try \"line1\\nline2\\ttab\\r\\nwindows\\0null\".write(\n            to: hostDir.appendingPathComponent(\"special.txt\"), atomically: true, encoding: .utf8)\n\n        let hostDestination = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"dir-rt-out\")\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Copy in.\n            try await container.copyIn(\n                from: hostDir,\n                to: URL(filePath: \"/tmp/dir-rt\")\n            )\n\n            // Copy back out.\n            try await container.copyOut(\n                from: URL(filePath: \"/tmp/dir-rt\"),\n                to: hostDestination\n            )\n\n            // Verify root.txt.\n            let rootContent = try String(contentsOf: hostDestination.appendingPathComponent(\"root.txt\"), encoding: .utf8)\n            guard rootContent == \"top level\" else {\n                throw IntegrationError.assert(msg: \"root.txt mismatch: '\\(rootContent)'\")\n            }\n\n            // Verify binary.bin.\n            let binaryContent = try Data(contentsOf: hostDestination.appendingPathComponent(\"binary.bin\"))\n            guard binaryContent == Data(repeating: 0xAB, count: 4096) else {\n                throw IntegrationError.assert(msg: \"binary.bin mismatch\")\n            }\n\n            // Verify deep nested file.\n            let deepOut =\n                hostDestination\n                .appendingPathComponent(\"a\")\n                .appendingPathComponent(\"b\")\n                .appendingPathComponent(\"c\")\n                .appendingPathComponent(\"deep.txt\")\n            let deepContent = try String(contentsOf: deepOut, encoding: .utf8)\n            guard deepContent == \"deep file\" else {\n                throw IntegrationError.assert(msg: \"deep.txt mismatch: '\\(deepContent)'\")\n            }\n\n            // Verify empty subdirectory exists.\n            var isDir: ObjCBool = false\n            let emptySubdirExists = FileManager.default.fileExists(\n                atPath: hostDestination.appendingPathComponent(\"empty-sub\").path,\n                isDirectory: &isDir\n            )\n            guard emptySubdirExists && isDir.boolValue else {\n                throw IntegrationError.assert(msg: \"empty-sub directory should exist after round trip\")\n            }\n\n            // Verify special characters file.\n            let specialContent = try String(\n                contentsOf: hostDestination.appendingPathComponent(\"special.txt\"), encoding: .utf8)\n            guard specialContent == \"line1\\nline2\\ttab\\r\\nwindows\\0null\" else {\n                throw IntegrationError.assert(msg: \"special.txt mismatch\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testCopyInCreateParents() async throws {\n        let id = \"test-copy-parents\"\n\n        let bs = try await bootstrap(id)\n\n        let testContent = \"create parents test\"\n        let hostFile = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"parents-test.txt\")\n        try testContent.write(to: hostFile, atomically: true, encoding: .utf8)\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Copy to a deeply nested path that doesn't exist yet.\n            try await container.copyIn(\n                from: hostFile,\n                to: URL(filePath: \"/tmp/a/b/c/d/e/file.txt\"),\n                createParents: true\n            )\n\n            // Verify the file got there.\n            let buffer = BufferWriter()\n            let exec = try await container.exec(\"verify-parents\") { config in\n                config.arguments = [\"cat\", \"/tmp/a/b/c/d/e/file.txt\"]\n                config.stdout = buffer\n            }\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"cat failed with status \\(status)\")\n            }\n            guard String(data: buffer.data, encoding: .utf8) == testContent else {\n                throw IntegrationError.assert(msg: \"content mismatch after copy with createParents\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testCopyFilePermissions() async throws {\n        let id = \"test-copy-perms\"\n\n        let bs = try await bootstrap(id)\n\n        let hostFile = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"perms-test.sh\")\n        try \"#!/bin/sh\\necho hello\".write(to: hostFile, atomically: true, encoding: .utf8)\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Copy with executable permissions.\n            try await container.copyIn(\n                from: hostFile,\n                to: URL(filePath: \"/tmp/perms-test.sh\"),\n                mode: 0o755\n            )\n\n            // Verify the file is executable by running it.\n            let buffer = BufferWriter()\n            let exec = try await container.exec(\"run-script\") { config in\n                config.arguments = [\"/tmp/perms-test.sh\"]\n                config.stdout = buffer\n            }\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"script execution failed with status \\(status)\")\n            }\n            let output = String(data: buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)\n            guard output == \"hello\" else {\n                throw IntegrationError.assert(msg: \"script output mismatch: '\\(output ?? \"nil\")'\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testCopyLargeDirectory() async throws {\n        let id = \"test-copy-large-dir\"\n\n        let bs = try await bootstrap(id)\n\n        // Create a directory with many files to stress the archive path.\n        let hostDir = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"large-dir\")\n        try FileManager.default.createDirectory(at: hostDir, withIntermediateDirectories: true)\n\n        let fileCount = 100\n        for i in 0..<fileCount {\n            let content = \"File \\(i): \\(String(repeating: String(i), count: 512))\"\n            try content.write(\n                to: hostDir.appendingPathComponent(\"file-\\(String(format: \"%03d\", i)).txt\"),\n                atomically: true,\n                encoding: .utf8\n            )\n        }\n\n        let hostDestination = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"large-dir-out\")\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            try await container.copyIn(\n                from: hostDir,\n                to: URL(filePath: \"/tmp/large-dir\")\n            )\n\n            // Verify file count in the guest.\n            let buffer = BufferWriter()\n            let exec = try await container.exec(\"count-files\") { config in\n                config.arguments = [\"sh\", \"-c\", \"ls /tmp/large-dir/*.txt | wc -l\"]\n                config.stdout = buffer\n            }\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"ls | wc -l failed with status \\(status)\")\n            }\n            let countStr = String(data: buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)\n            guard countStr == \"\\(fileCount)\" else {\n                throw IntegrationError.assert(msg: \"expected \\(fileCount) files in guest, got '\\(countStr ?? \"nil\")'\")\n            }\n\n            // Copy back out.\n            try await container.copyOut(\n                from: URL(filePath: \"/tmp/large-dir\"),\n                to: hostDestination\n            )\n\n            // Spot-check a few files.\n            for i in [0, fileCount / 2, fileCount - 1] {\n                let expectedContent = \"File \\(i): \\(String(repeating: String(i), count: 512))\"\n                let actualContent = try String(\n                    contentsOf: hostDestination.appendingPathComponent(\"file-\\(String(format: \"%03d\", i)).txt\"),\n                    encoding: .utf8\n                )\n                guard actualContent == expectedContent else {\n                    throw IntegrationError.assert(msg: \"file-\\(String(format: \"%03d\", i)).txt content mismatch after round trip\")\n                }\n            }\n\n            // Verify total file count on host.\n            let outFiles = try FileManager.default.contentsOfDirectory(atPath: hostDestination.path)\n            guard outFiles.count == fileCount else {\n                throw IntegrationError.assert(\n                    msg: \"expected \\(fileCount) files on host after copyOut, got \\(outFiles.count)\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testReadOnlyRootfs() async throws {\n        let id = \"test-readonly-rootfs\"\n\n        let bs = try await bootstrap(id)\n        var rootfs = bs.rootfs\n        rootfs.options.append(\"ro\")\n        let container = try LinuxContainer(id, rootfs: rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"touch\", \"/testfile\"]\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        // touch should fail on a read-only rootfs\n        guard status.exitCode != 0 else {\n            throw IntegrationError.assert(msg: \"touch should have failed on read-only rootfs\")\n        }\n    }\n\n    func testReadOnlyRootfsHostsFileWritten() async throws {\n        let id = \"test-readonly-rootfs-hosts\"\n\n        let bs = try await bootstrap(id)\n        var rootfs = bs.rootfs\n        rootfs.options.append(\"ro\")\n        let buffer = BufferWriter()\n        let entry = Hosts.Entry.localHostIPV4(comment: \"ReadOnlyTest\")\n        let container = try LinuxContainer(id, rootfs: rootfs, vmm: bs.vmm) { config in\n            // Verify /etc/hosts was written before rootfs was remounted read-only\n            config.process.arguments = [\"cat\", \"/etc/hosts\"]\n            config.process.stdout = buffer\n            config.hosts = Hosts(entries: [entry])\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"cat /etc/hosts failed with status \\(status)\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        guard output.contains(\"ReadOnlyTest\") else {\n            throw IntegrationError.assert(msg: \"expected /etc/hosts to contain our entry, got: \\(output)\")\n        }\n    }\n\n    func testReadOnlyRootfsDNSConfigured() async throws {\n        let id = \"test-readonly-rootfs-dns\"\n\n        let bs = try await bootstrap(id)\n        var rootfs = bs.rootfs\n        rootfs.options.append(\"ro\")\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: rootfs, vmm: bs.vmm) { config in\n            // Verify /etc/resolv.conf was written before rootfs was remounted read-only\n            config.process.arguments = [\"cat\", \"/etc/resolv.conf\"]\n            config.process.stdout = buffer\n            config.dns = DNS(nameservers: [\"8.8.8.8\", \"8.8.4.4\"])\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"cat /etc/resolv.conf failed with status \\(status)\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        guard output.contains(\"8.8.8.8\") && output.contains(\"8.8.4.4\") else {\n            throw IntegrationError.assert(msg: \"expected /etc/resolv.conf to contain DNS servers, got: \\(output)\")\n        }\n    }\n\n    func testLargeStdinInput() async throws {\n        let id = \"test-large-stdin-input\"\n\n        let bs = try await bootstrap(id)\n\n        let inputSize = 128 * 1024\n        let inputData = Data(repeating: 0x41, count: inputSize)  // 'A' repeated\n\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"cat\"]\n            config.process.stdin = StdinBuffer(data: inputData)\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let status = try await container.wait()\n            try await container.stop()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n            }\n\n            guard buffer.data.count == inputSize else {\n                throw IntegrationError.assert(\n                    msg: \"output size \\(buffer.data.count) != input size \\(inputSize)\")\n            }\n\n            guard buffer.data == inputData else {\n                throw IntegrationError.assert(msg: \"output data does not match input data\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testExecLargeStdinInput() async throws {\n        let id = \"test-exec-large-stdin-input\"\n        let bs = try await bootstrap(id)\n\n        let inputSize = 128 * 1024\n        let inputData = Data(repeating: 0x42, count: inputSize)\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let buffer = BufferWriter()\n            let exec = try await container.exec(\"large-stdin-exec\") { config in\n                config.arguments = [\"cat\"]\n                config.stdin = StdinBuffer(data: inputData)\n                config.stdout = buffer\n            }\n\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"exec status \\(status) != 0\")\n            }\n\n            guard buffer.data.count == inputSize else {\n                throw IntegrationError.assert(msg: \"output size \\(buffer.data.count) != \\(inputSize)\")\n            }\n\n            guard buffer.data == inputData else {\n                throw IntegrationError.assert(msg: \"output data mismatch\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testExecCustomPathResolution() async throws {\n        let id = \"test-exec-custom-path\"\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/sleep\", \"1000\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Create a script in a non-standard directory\n            let setup = try await container.exec(\"setup\") { config in\n                config.arguments = [\n                    \"sh\", \"-c\",\n                    \"mkdir -p /tmp/custom-bin && printf '#!/bin/sh\\\\necho CUSTOM_PATH_OK' > /tmp/custom-bin/mytest && chmod +x /tmp/custom-bin/mytest\",\n                ]\n            }\n            try await setup.start()\n            let setupStatus = try await setup.wait()\n            try await setup.delete()\n            guard setupStatus.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"setup failed: \\(setupStatus)\")\n            }\n\n            // Exec bare command with custom PATH — this exercises ExecCommand.swift\n            let buffer = BufferWriter()\n            let exec = try await container.exec(\"custom-path\") { config in\n                config.arguments = [\"mytest\"]\n                config.environmentVariables = [\"PATH=/tmp/custom-bin\"]\n                config.stdout = buffer\n            }\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"exec with custom PATH failed: \\(status)\")\n            }\n\n            guard let output = String(data: buffer.data, encoding: .utf8) else {\n                throw IntegrationError.assert(msg: \"failed to read output\")\n            }\n            guard output.contains(\"CUSTOM_PATH_OK\") else {\n                throw IntegrationError.assert(msg: \"expected CUSTOM_PATH_OK, got: \\(output)\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testStdinExplicitClose() async throws {\n        let id = \"test-stdin-explicit-close\"\n        let bs = try await bootstrap(id)\n\n        let inputData = \"explicit close test\\n\".data(using: .utf8)!\n        let buffer = BufferWriter()\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let exec = try await container.exec(\"stdin-close-exec\") { config in\n                config.arguments = [\"head\", \"-n\", \"1\"]\n                config.stdin = StdinBuffer(data: inputData)\n                config.stdout = buffer\n            }\n\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"exec status \\(status) != 0\")\n            }\n\n            guard buffer.data == inputData else {\n                throw IntegrationError.assert(msg: \"output mismatch\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testStdinBinaryData() async throws {\n        let id = \"test-stdin-binary-data\"\n        let bs = try await bootstrap(id)\n\n        var inputData = Data()\n        for i: UInt8 in 0...255 {\n            inputData.append(contentsOf: [UInt8](repeating: i, count: 256))\n        }\n\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"cat\"]\n            config.process.stdin = StdinBuffer(data: inputData)\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let status = try await container.wait()\n            try await container.stop()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n            }\n\n            guard buffer.data == inputData else {\n                throw IntegrationError.assert(msg: \"binary data mismatch\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testStdinMultipleChunks() async throws {\n        let id = \"test-stdin-multiple-chunks\"\n        let bs = try await bootstrap(id)\n\n        let chunks = (0..<10).map { i in\n            Data(repeating: UInt8(0x30 + i), count: 10 * 1024)\n        }\n        let expectedData = chunks.reduce(Data()) { $0 + $1 }\n\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"cat\"]\n            config.process.stdin = ChunkedStdinBuffer(chunks: chunks, delayMs: 10)\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let status = try await container.wait()\n            try await container.stop()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n            }\n\n            guard buffer.data == expectedData else {\n                throw IntegrationError.assert(msg: \"chunked data mismatch\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testStdinVeryLarge() async throws {\n        let id = \"test-stdin-very-large\"\n        let bs = try await bootstrap(id)\n\n        let inputSize = 10 * 1024 * 1024\n        let inputData = Data(repeating: 0x58, count: inputSize)\n\n        let stdout = DiscardingWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"wc\", \"-c\"]\n            config.process.stdin = StdinBuffer(data: inputData)\n            config.process.stdout = stdout\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let status = try await container.wait()\n            try await container.stop()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n            }\n\n            guard stdout.count > 0 else {\n                throw IntegrationError.assert(msg: \"no output from wc\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    @available(macOS 26.0, *)\n    func testInterfaceMTU() async throws {\n        let id = \"test-interface-mtu\"\n        let bs = try await bootstrap(id)\n\n        let customMTU: UInt32 = 1400\n        var network = try VmnetNetwork()\n        defer {\n            try? network.releaseInterface(id)\n        }\n\n        guard let interface = try network.createInterface(id, mtu: customMTU) else {\n            throw IntegrationError.assert(msg: \"failed to create network interface\")\n        }\n\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.interfaces = [interface]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Check the MTU of eth0\n            let exec = try await container.exec(\"check-mtu\") { config in\n                config.arguments = [\"ip\", \"link\", \"show\", \"eth0\"]\n                config.stdout = buffer\n            }\n\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"ip link show failed with status \\(status)\")\n            }\n\n            guard let output = String(data: buffer.data, encoding: .utf8) else {\n                throw IntegrationError.assert(msg: \"failed to convert output to UTF8\")\n            }\n\n            // Output should contain \"mtu 1400\"\n            guard output.contains(\"mtu \\(customMTU)\") else {\n                throw IntegrationError.assert(\n                    msg: \"expected MTU \\(customMTU) in output, got: \\(output)\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testSingleFileMount() async throws {\n        let id = \"test-single-file-mount\"\n\n        let bs = try await bootstrap(id)\n\n        // Create a temp file with known content\n        let testContent = \"Hello from single file mount!\"\n        let hostFile = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"config.txt\")\n        try testContent.write(to: hostFile, atomically: true, encoding: .utf8)\n\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"cat\", \"/etc/myconfig.txt\"]\n            // Mount a single file using virtiofs share\n            config.mounts.append(.share(source: hostFile.path, destination: \"/etc/myconfig.txt\"))\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let status = try await container.wait()\n            try await container.stop()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n            }\n\n            guard let output = String(data: buffer.data, encoding: .utf8) else {\n                throw IntegrationError.assert(msg: \"failed to convert output to UTF8\")\n            }\n\n            guard output == testContent else {\n                throw IntegrationError.assert(\n                    msg: \"expected '\\(testContent)', got '\\(output)'\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testSingleFileMountReadOnly() async throws {\n        let id = \"test-single-file-mount-readonly\"\n\n        let bs = try await bootstrap(id)\n\n        // Create a temp file with known content\n        let testContent = \"Read-only file content\"\n        let hostFile = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"readonly.txt\")\n        try testContent.write(to: hostFile, atomically: true, encoding: .utf8)\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            // Mount a single file as read-only\n            config.mounts.append(.share(source: hostFile.path, destination: \"/etc/readonly.txt\", options: [\"ro\"]))\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // First verify we can read the file\n            let readBuffer = BufferWriter()\n            let readExec = try await container.exec(\"read-file\") { config in\n                config.arguments = [\"cat\", \"/etc/readonly.txt\"]\n                config.stdout = readBuffer\n            }\n            try await readExec.start()\n            var status = try await readExec.wait()\n            try await readExec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"read status \\(status) != 0\")\n            }\n\n            guard String(data: readBuffer.data, encoding: .utf8) == testContent else {\n                throw IntegrationError.assert(msg: \"file content mismatch\")\n            }\n\n            // Now try to write to the file - should fail\n            let writeExec = try await container.exec(\"write-file\") { config in\n                config.arguments = [\"sh\", \"-c\", \"echo 'modified' > /etc/readonly.txt\"]\n            }\n            try await writeExec.start()\n            status = try await writeExec.wait()\n            try await writeExec.delete()\n\n            // Write should fail on a read-only mount\n            guard status.exitCode != 0 else {\n                throw IntegrationError.assert(msg: \"write should have failed on read-only mount\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testSingleFileMountWriteBack() async throws {\n        let id = \"test-single-file-mount-write-back\"\n\n        let bs = try await bootstrap(id)\n\n        // Create a temp file with initial content\n        let initialContent = \"initial content\"\n        let hostFile = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"writeable.txt\")\n        try initialContent.write(to: hostFile, atomically: true, encoding: .utf8)\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            // Mount a single file (writable by default)\n            config.mounts.append(.share(source: hostFile.path, destination: \"/etc/writeable.txt\"))\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Write new content from inside the container\n            let newContent = \"modified from container\"\n            let writeExec = try await container.exec(\"write-file\") { config in\n                config.arguments = [\"sh\", \"-c\", \"echo -n '\\(newContent)' > /etc/writeable.txt\"]\n            }\n            try await writeExec.start()\n            let status = try await writeExec.wait()\n            try await writeExec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"write status \\(status) != 0\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n\n            let hostContent = try String(contentsOf: hostFile, encoding: .utf8)\n            guard hostContent == newContent else {\n                throw IntegrationError.assert(\n                    msg: \"expected '\\(newContent)' on host, got '\\(hostContent)'\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testSingleFileMountSymlink() async throws {\n        let id = \"test-single-file-mount-symlink\"\n\n        let bs = try await bootstrap(id)\n\n        // Create a temp directory with a real file and a symlink to it\n        let tempDir = FileManager.default.uniqueTemporaryDirectory(create: true)\n        let realFile = tempDir.appendingPathComponent(\"realfile.txt\")\n        let symlinkFile = tempDir.appendingPathComponent(\"symlink.txt\")\n\n        let initialContent = \"content via symlink\"\n        try initialContent.write(to: realFile, atomically: true, encoding: .utf8)\n        try FileManager.default.createSymbolicLink(at: symlinkFile, withDestinationURL: realFile)\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            // Mount the symlink (should resolve to real file)\n            config.mounts.append(.share(source: symlinkFile.path, destination: \"/etc/config.txt\"))\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Read the file to verify content\n            let readBuffer = BufferWriter()\n            let readExec = try await container.exec(\"read-file\") { config in\n                config.arguments = [\"cat\", \"/etc/config.txt\"]\n                config.stdout = readBuffer\n            }\n            try await readExec.start()\n            var status = try await readExec.wait()\n            try await readExec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"read status \\(status) != 0\")\n            }\n\n            guard String(data: readBuffer.data, encoding: .utf8) == initialContent else {\n                throw IntegrationError.assert(msg: \"content mismatch on read\")\n            }\n\n            // Write new content from container\n            let newContent = \"modified via symlink mount\"\n            let writeExec = try await container.exec(\"write-file\") { config in\n                config.arguments = [\"sh\", \"-c\", \"echo -n '\\(newContent)' > /etc/config.txt\"]\n            }\n            try await writeExec.start()\n            status = try await writeExec.wait()\n            try await writeExec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"write status \\(status) != 0\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n\n            // Verify the REAL file (not symlink) was modified on the host\n            let hostContent = try String(contentsOf: realFile, encoding: .utf8)\n            guard hostContent == newContent else {\n                throw IntegrationError.assert(\n                    msg: \"expected '\\(newContent)' in real file, got '\\(hostContent)'\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testRLimitOpenFiles() async throws {\n        let id = \"test-rlimit-open-files\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sh\", \"-c\", \"ulimit -n\"]\n            config.process.rlimits = [\n                LinuxRLimit(kind: .openFiles, hard: 2048, soft: 1024)\n            ]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        // ulimit -n returns the soft limit\n        guard output == \"1024\" else {\n            throw IntegrationError.assert(msg: \"expected soft limit '1024', got '\\(output)'\")\n        }\n    }\n\n    func testRLimitMultiple() async throws {\n        let id = \"test-rlimit-multiple\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            // Read /proc/self/limits to verify multiple rlimits are set\n            config.process.arguments = [\"cat\", \"/proc/self/limits\"]\n            config.process.rlimits = [\n                LinuxRLimit(kind: .openFiles, hard: 4096, soft: 2048),\n                LinuxRLimit(kind: .stackSize, hard: 16_777_216, soft: 8_388_608),\n                LinuxRLimit(kind: .coreFileSize, hard: 0, soft: 0),\n            ]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        // Parse /proc/self/limits and verify the values\n        // Format: \"Limit Name                Soft Limit           Hard Limit           Units\"\n        let lines = output.split(separator: \"\\n\")\n\n        // Helper to find and verify a limit line\n        func verifyLimit(name: String, expectedSoft: String, expectedHard: String) throws {\n            guard let line = lines.first(where: { $0.contains(name) }) else {\n                throw IntegrationError.assert(msg: \"limit '\\(name)' not found in output\")\n            }\n            let parts = line.split(whereSeparator: { $0.isWhitespace }).map(String.init)\n            // The line format varies, but soft and hard are typically the last numeric values before units\n            guard parts.contains(expectedSoft) && parts.contains(expectedHard) else {\n                throw IntegrationError.assert(\n                    msg: \"limit '\\(name)' expected soft=\\(expectedSoft) hard=\\(expectedHard), got: \\(line)\")\n            }\n        }\n\n        try verifyLimit(name: \"Max open files\", expectedSoft: \"2048\", expectedHard: \"4096\")\n        try verifyLimit(name: \"Max stack size\", expectedSoft: \"8388608\", expectedHard: \"16777216\")\n        try verifyLimit(name: \"Max core file size\", expectedSoft: \"0\", expectedHard: \"0\")\n    }\n\n    func testRLimitExec() async throws {\n        let id = \"test-rlimit-exec\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Exec a process with rlimits set\n            let buffer = BufferWriter()\n            let exec = try await container.exec(\"rlimit-exec\") { config in\n                config.arguments = [\"sh\", \"-c\", \"ulimit -n\"]\n                config.rlimits = [\n                    LinuxRLimit(kind: .openFiles, hard: 512, soft: 256)\n                ]\n                config.stdout = buffer\n            }\n\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"exec status \\(status) != 0\")\n            }\n\n            guard let output = String(data: buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {\n                throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n            }\n\n            guard output == \"256\" else {\n                throw IntegrationError.assert(msg: \"expected soft limit '256', got '\\(output)'\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testDuplicateVirtiofsMount() async throws {\n        let id = \"test-duplicate-virtiofs-mount\"\n\n        let bs = try await bootstrap(id)\n\n        // Create a temp directory with a file\n        let sharedDir = FileManager.default.uniqueTemporaryDirectory(create: true)\n        try \"shared content\".write(to: sharedDir.appendingPathComponent(\"data.txt\"), atomically: true, encoding: .utf8)\n\n        let buffer1 = BufferWriter()\n        let buffer2 = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            // Mount the same source directory to two different destinations\n            config.mounts.append(.share(source: sharedDir.path, destination: \"/mnt1\"))\n            config.mounts.append(.share(source: sharedDir.path, destination: \"/mnt2\"))\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Verify both mounts work. Read from /mnt1, then /mnt2\n            let exec1 = try await container.exec(\"read-mnt1\") { config in\n                config.arguments = [\"cat\", \"/mnt1/data.txt\"]\n                config.stdout = buffer1\n            }\n            try await exec1.start()\n            var status = try await exec1.wait()\n            try await exec1.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"read from /mnt1 failed with status \\(status)\")\n            }\n\n            guard String(data: buffer1.data, encoding: .utf8) == \"shared content\" else {\n                throw IntegrationError.assert(msg: \"unexpected content from /mnt1\")\n            }\n\n            let exec2 = try await container.exec(\"read-mnt2\") { config in\n                config.arguments = [\"cat\", \"/mnt2/data.txt\"]\n                config.stdout = buffer2\n            }\n            try await exec2.start()\n            status = try await exec2.wait()\n            try await exec2.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"read from /mnt2 failed with status \\(status)\")\n            }\n\n            guard String(data: buffer2.data, encoding: .utf8) == \"shared content\" else {\n                throw IntegrationError.assert(msg: \"unexpected content from /mnt2\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testDuplicateVirtiofsMountViaSymlink() async throws {\n        let id = \"test-duplicate-virtiofs-mount-symlink\"\n\n        let bs = try await bootstrap(id)\n\n        // Create a temp directory with a file, and a symlink to the same directory\n        let tempDir = FileManager.default.uniqueTemporaryDirectory(create: true)\n        let realDir = tempDir.appendingPathComponent(\"realdir\")\n        let symlinkDir = tempDir.appendingPathComponent(\"symlinkdir\")\n\n        try FileManager.default.createDirectory(at: realDir, withIntermediateDirectories: true)\n        try \"symlink test content\".write(to: realDir.appendingPathComponent(\"file.txt\"), atomically: true, encoding: .utf8)\n        try FileManager.default.createSymbolicLink(at: symlinkDir, withDestinationURL: realDir)\n\n        let buffer1 = BufferWriter()\n        let buffer2 = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.mounts.append(.share(source: realDir.path, destination: \"/mnt1\"))\n            config.mounts.append(.share(source: symlinkDir.path, destination: \"/mnt2\"))\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            // This should succeed as the symlink should resolve to the same directory\n            try await container.create()\n            try await container.start()\n\n            let exec1 = try await container.exec(\"read-mnt1\") { config in\n                config.arguments = [\"cat\", \"/mnt1/file.txt\"]\n                config.stdout = buffer1\n            }\n            try await exec1.start()\n            var status = try await exec1.wait()\n            try await exec1.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"read from /mnt1 failed with status \\(status)\")\n            }\n\n            guard String(data: buffer1.data, encoding: .utf8) == \"symlink test content\" else {\n                throw IntegrationError.assert(msg: \"unexpected content from /mnt1\")\n            }\n\n            // Verify mount via symlink works now\n            let exec2 = try await container.exec(\"read-mnt2\") { config in\n                config.arguments = [\"cat\", \"/mnt2/file.txt\"]\n                config.stdout = buffer2\n            }\n            try await exec2.start()\n            status = try await exec2.wait()\n            try await exec2.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"read from /mnt2 failed with status \\(status)\")\n            }\n\n            guard String(data: buffer2.data, encoding: .utf8) == \"symlink test content\" else {\n                throw IntegrationError.assert(msg: \"unexpected content from /mnt2\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testWritableLayer() async throws {\n        let id = \"test-writable-layer\"\n\n        let bs = try await bootstrap(id)\n\n        let writableLayerPath = Self.testDir.appending(component: \"\\(id)-writable.ext4\")\n        try? FileManager.default.removeItem(at: writableLayerPath)\n        let filesystem = try EXT4.Formatter(FilePath(writableLayerPath.absolutePath()), minDiskSize: 512.mib())\n        try filesystem.close()\n        let writableLayer = Mount.block(\n            format: \"ext4\",\n            source: writableLayerPath.absolutePath(),\n            destination: \"/\",\n            options: []\n        )\n\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, writableLayer: writableLayer, vmm: bs.vmm) { config in\n            // Write a file, then read it back to verify writes work\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"echo 'writable layer test' > /tmp/testfile && cat /tmp/testfile\"]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process failed with status \\(status)\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        guard output.trimmingCharacters(in: .whitespacesAndNewlines) == \"writable layer test\" else {\n            throw IntegrationError.assert(msg: \"unexpected output: \\(output)\")\n        }\n    }\n\n    func testWritableLayerPreservesLowerLayer() async throws {\n        let id = \"test-writable-layer-preserves-lower\"\n\n        let bs = try await bootstrap(id)\n\n        let writableLayerPath = Self.testDir.appending(component: \"\\(id)-writable.ext4\")\n        try? FileManager.default.removeItem(at: writableLayerPath)\n        let filesystem = try EXT4.Formatter(FilePath(writableLayerPath.absolutePath()), minDiskSize: 512.mib())\n        try filesystem.close()\n        let writableLayer = Mount.block(\n            format: \"ext4\",\n            source: writableLayerPath.absolutePath(),\n            destination: \"/\",\n            options: []\n        )\n\n        // Get the size of /bin/sh before any modifications\n        let buffer1 = BufferWriter()\n        let container1 = try LinuxContainer(\"\\(id)-1\", rootfs: bs.rootfs, writableLayer: writableLayer, vmm: bs.vmm) { config in\n            // Modify a file in /bin. This should go in the writable layer.\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"ls -la /bin/sh && echo 'modified' > /bin/test-file\"]\n            config.process.stdout = buffer1\n            config.bootLog = bs.bootLog\n        }\n\n        try await container1.create()\n        try await container1.start()\n        let status1 = try await container1.wait()\n        try await container1.stop()\n\n        guard status1.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"first container failed with status \\(status1)\")\n        }\n\n        // Now run a second container with the SAME rootfs but without the writable layer\n        // The /bin/test-file should NOT exist because it was written to the writable layer\n        let buffer2 = BufferWriter()\n        let container2 = try LinuxContainer(\"\\(id)-2\", rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"test -f /bin/test-file && echo 'exists' || echo 'not-exists'\"]\n            config.process.stdout = buffer2\n            config.bootLog = bs.bootLog\n        }\n\n        try await container2.create()\n        try await container2.start()\n        let status2 = try await container2.wait()\n        try await container2.stop()\n\n        guard status2.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"second container failed with status \\(status2)\")\n        }\n\n        guard let output2 = String(data: buffer2.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        guard output2.trimmingCharacters(in: .whitespacesAndNewlines) == \"not-exists\" else {\n            throw IntegrationError.assert(msg: \"expected 'not-exists' but got: \\(output2)\")\n        }\n    }\n\n    func testWritableLayerReadsFromLower() async throws {\n        let id = \"test-writable-layer-reads-lower\"\n\n        let bs = try await bootstrap(id)\n\n        let writableLayerPath = Self.testDir.appending(component: \"\\(id)-writable.ext4\")\n        try? FileManager.default.removeItem(at: writableLayerPath)\n        let filesystem = try EXT4.Formatter(FilePath(writableLayerPath.absolutePath()), minDiskSize: 512.mib())\n        try filesystem.close()\n        let writableLayer = Mount.block(\n            format: \"ext4\",\n            source: writableLayerPath.absolutePath(),\n            destination: \"/\",\n            options: []\n        )\n\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, writableLayer: writableLayer, vmm: bs.vmm) { config in\n            config.process.arguments = [\"head\", \"-1\", \"/etc/passwd\"]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process failed with status \\(status)\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        // Alpine's first line of /etc/passwd should be root\n        guard output.hasPrefix(\"root:\") else {\n            throw IntegrationError.assert(msg: \"expected /etc/passwd to start with 'root:', got: \\(output)\")\n        }\n    }\n\n    func testWritableLayerWithReadOnlyLower() async throws {\n        let id = \"test-writable-layer-ro-lower\"\n\n        let bs = try await bootstrap(id)\n        var rootfs = bs.rootfs\n        rootfs.options.append(\"ro\")\n\n        let writableLayerPath = Self.testDir.appending(component: \"\\(id)-writable.ext4\")\n        try? FileManager.default.removeItem(at: writableLayerPath)\n        let filesystem = try EXT4.Formatter(FilePath(writableLayerPath.absolutePath()), minDiskSize: 512.mib())\n        try filesystem.close()\n        let writableLayer = Mount.block(\n            format: \"ext4\",\n            source: writableLayerPath.absolutePath(),\n            destination: \"/\",\n            options: []\n        )\n\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: rootfs, writableLayer: writableLayer, vmm: bs.vmm) { config in\n            // Even though lower layer is ro, writes should succeed via overlay\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"echo 'overlay write test' > /tmp/test && cat /tmp/test\"]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process failed with status \\(status)\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        guard output.trimmingCharacters(in: .whitespacesAndNewlines) == \"overlay write test\" else {\n            throw IntegrationError.assert(msg: \"unexpected output: \\(output)\")\n        }\n    }\n\n    func testWritableLayerSize() async throws {\n        let id = \"test-writable-layer-size\"\n\n        let bs = try await bootstrap(id)\n\n        // Create a 1 GiB writable layer\n        let expectedSizeBytes: UInt64 = 1.gib()\n        let writableLayerPath = Self.testDir.appending(component: \"\\(id)-writable.ext4\")\n        try? FileManager.default.removeItem(at: writableLayerPath)\n        let filesystem = try EXT4.Formatter(FilePath(writableLayerPath.absolutePath()), minDiskSize: expectedSizeBytes)\n        try filesystem.close()\n        let writableLayer = Mount.block(\n            format: \"ext4\",\n            source: writableLayerPath.absolutePath(),\n            destination: \"/\",\n            options: []\n        )\n\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, writableLayer: writableLayer, vmm: bs.vmm) { config in\n            // Use df to check the available space on the root filesystem\n            // The overlay will report the size of the upper layer's backing store\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"df -B1 / | tail -1 | awk '{print $2}'\"]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process failed with status \\(status)\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        guard let reportedSize = UInt64(output.trimmingCharacters(in: .whitespacesAndNewlines)) else {\n            throw IntegrationError.assert(msg: \"failed to parse df output as UInt64: \\(output)\")\n        }\n\n        // The reported size should be close to our expected size (within 10%)\n        let minExpected: UInt64 = (expectedSizeBytes * 90) / 100\n        let maxExpected: UInt64 = (expectedSizeBytes * 110) / 100\n\n        guard reportedSize >= minExpected && reportedSize <= maxExpected else {\n            throw IntegrationError.assert(msg: \"expected size ~\\(expectedSizeBytes) bytes, but df reported \\(reportedSize) bytes\")\n        }\n    }\n\n    func testWritableLayerWithDNSAndHosts() async throws {\n        let id = \"test-writable-layer-dns-hosts\"\n\n        let bs = try await bootstrap(id)\n\n        let writableLayerPath = Self.testDir.appending(component: \"\\(id)-writable.ext4\")\n        try? FileManager.default.removeItem(at: writableLayerPath)\n        let filesystem = try EXT4.Formatter(FilePath(writableLayerPath.absolutePath()), minDiskSize: 512.mib())\n        try filesystem.close()\n        let writableLayer = Mount.block(\n            format: \"ext4\",\n            source: writableLayerPath.absolutePath(),\n            destination: \"/\",\n            options: []\n        )\n\n        let buffer = BufferWriter()\n        let dnsEntry = \"8.8.8.8\"\n        let hostsEntry = Hosts.Entry.localHostIPV4(comment: \"WritableLayerTest\")\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, writableLayer: writableLayer, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"cat /etc/resolv.conf && echo '---' && cat /etc/hosts\"]\n            config.process.stdout = buffer\n            config.dns = DNS(nameservers: [dnsEntry])\n            config.hosts = Hosts(entries: [hostsEntry])\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process failed with status \\(status)\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        guard output.contains(dnsEntry) else {\n            throw IntegrationError.assert(msg: \"expected /etc/resolv.conf to contain \\(dnsEntry), got: \\(output)\")\n        }\n\n        guard output.contains(\"WritableLayerTest\") else {\n            throw IntegrationError.assert(msg: \"expected /etc/hosts to contain our entry, got: \\(output)\")\n        }\n    }\n\n    func testUseInitBasic() async throws {\n        let id = \"test-use-init-basic\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/echo\", \"hello from init\"]\n            config.process.stdout = buffer\n            config.useInit = true\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard String(data: buffer.data, encoding: .utf8) == \"hello from init\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"expected 'hello from init', got '\\(String(data: buffer.data, encoding: .utf8) ?? \"nil\")'\")\n        }\n    }\n\n    func testUseInitExitCodePropagation() async throws {\n        let id = \"test-use-init-exit-code\"\n\n        let bs = try await bootstrap(id)\n\n        // Test exit code 0\n        var container = try LinuxContainer(\"\\(id)-success\", rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/true\"]\n            config.useInit = true\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n        var status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"expected exit code 0, got \\(status.exitCode)\")\n        }\n\n        // Test non-zero exit code\n        container = try LinuxContainer(\"\\(id)-failure\", rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/false\"]\n            config.useInit = true\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n        status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 1 else {\n            throw IntegrationError.assert(msg: \"expected exit code 1, got \\(status.exitCode)\")\n        }\n\n        // Test custom exit code\n        container = try LinuxContainer(\"\\(id)-custom\", rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sh\", \"-c\", \"exit 42\"]\n            config.useInit = true\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n        status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 42 else {\n            throw IntegrationError.assert(msg: \"expected exit code 42, got \\(status.exitCode)\")\n        }\n    }\n\n    func testUseInitSignalForwarding() async throws {\n        let id = \"test-use-init-signal\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"300\"]\n            config.useInit = true\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            try await Task.sleep(for: .milliseconds(100))\n\n            try await container.kill(SIGTERM)\n\n            let status = try await container.wait(timeoutInSeconds: 5)\n            try await container.stop()\n\n            // SIGTERM should result in exit code 128 + 15 = 143\n            guard status.exitCode == 143 else {\n                throw IntegrationError.assert(msg: \"expected exit code 143 (SIGTERM), got \\(status.exitCode)\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testUseInitZombieReaping() async throws {\n        let id = \"test-use-init-zombie-reaping\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            // This script creates an orphaned process that init must reap.\n            // The subshell exits immediately, orphaning the sleep process.\n            // Init should reap it when it exits.\n            config.process.arguments = [\n                \"/bin/sh\", \"-c\",\n                \"\"\"\n                # Create orphans: subshell exits before its children\n                (/bin/sleep 0.1 &)\n                (/bin/sleep 0.1 &)\n                # Wait for orphans to complete\n                /bin/sleep 0.3\n                # Check for zombie processes (Z state)\n                zombies=$(ps -eo stat 2>/dev/null | grep -c '^Z' || echo 0)\n                echo \"zombie_count:$zombies\"\n                \"\"\",\n            ]\n            config.process.stdout = buffer\n            config.useInit = true\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let status = try await container.wait()\n            try await container.stop()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n            }\n\n            guard let output = String(data: buffer.data, encoding: .utf8) else {\n                throw IntegrationError.assert(msg: \"failed to convert output to UTF8\")\n            }\n\n            // Should report 0 zombies\n            guard output.contains(\"zombie_count:0\") else {\n                throw IntegrationError.assert(msg: \"expected zero zombies, got: \\(output)\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testUseInitWithTerminal() async throws {\n        let id = \"test-use-init-terminal\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"tty && echo 'has tty'\"]\n            config.process.terminal = true\n            config.process.stdout = buffer\n            config.useInit = true\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert output to UTF8\")\n        }\n\n        guard output.contains(\"has tty\") else {\n            throw IntegrationError.assert(msg: \"expected 'has tty' in output, got: \\(output)\")\n        }\n    }\n\n    func testUseInitWithStdin() async throws {\n        let id = \"test-use-init-stdin\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"cat\"]\n            config.process.stdin = StdinBuffer(data: \"input through init\\n\".data(using: .utf8)!)\n            config.process.stdout = buffer\n            config.useInit = true\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard String(data: buffer.data, encoding: .utf8) == \"input through init\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"expected 'input through init', got '\\(String(data: buffer.data, encoding: .utf8) ?? \"nil\")'\")\n        }\n    }\n\n    @available(macOS 26.0, *)\n    func testNetworkingDisabled() async throws {\n        let id = \"test-networking-disabled\"\n        let bs = try await bootstrap(id)\n\n        let network = try VmnetNetwork()\n        var manager = try ContainerManager(vmm: bs.vmm, network: network)\n        defer {\n            try? manager.delete(id)\n        }\n\n        let buffer = BufferWriter()\n        let container = try await manager.create(\n            id,\n            image: bs.image,\n            rootfs: bs.rootfs,\n            networking: false\n        ) { config in\n            config.process.arguments = [\"ls\", \"-1\", \"/sys/class/net/\"]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let status = try await container.wait()\n            try await container.stop()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"ls /sys/class/net/ failed with status \\(status)\")\n            }\n\n            guard let output = String(data: buffer.data, encoding: .utf8) else {\n                throw IntegrationError.assert(msg: \"failed to convert output to UTF8\")\n            }\n\n            // With networking disabled check we don't have an eth0.\n            let interfaces = output.trimmingCharacters(in: .whitespacesAndNewlines)\n                .split(separator: \"\\n\")\n                .map { $0.trimmingCharacters(in: .whitespaces) }\n\n            guard !interfaces.contains(\"eth0\") else {\n                throw IntegrationError.assert(\n                    msg: \"expected no 'eth0' interface\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    @available(macOS 26.0, *)\n    func testNetworkingEnabled() async throws {\n        let id = \"test-networking-enabled\"\n        let bs = try await bootstrap(id)\n\n        let network = try VmnetNetwork()\n        var manager = try ContainerManager(vmm: bs.vmm, network: network)\n        defer {\n            try? manager.delete(id)\n        }\n\n        let buffer = BufferWriter()\n        let container = try await manager.create(\n            id,\n            image: bs.image,\n            rootfs: bs.rootfs\n        ) { config in\n            config.process.arguments = [\"ls\", \"-1\", \"/sys/class/net/\"]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let status = try await container.wait()\n            try await container.stop()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"ls /sys/class/net/ failed with status \\(status)\")\n            }\n\n            guard let output = String(data: buffer.data, encoding: .utf8) else {\n                throw IntegrationError.assert(msg: \"failed to convert output to UTF8\")\n            }\n\n            // With networking enabled (default), eth0 should be present alongside lo\n            let interfaces = Set(\n                output.trimmingCharacters(in: .whitespacesAndNewlines)\n                    .split(separator: \"\\n\")\n                    .map { $0.trimmingCharacters(in: .whitespaces) }\n            )\n            guard interfaces.contains(\"lo\") else {\n                throw IntegrationError.assert(msg: \"expected 'lo' interface, got: \\(interfaces)\")\n            }\n            guard interfaces.contains(\"eth0\") else {\n                throw IntegrationError.assert(msg: \"expected 'eth0' interface, got: \\(interfaces)\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testSysctl() async throws {\n        let id = \"test-container-sysctl\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.sysctl = [\n                \"net.core.somaxconn\": \"4096\"\n            ]\n            config.process.arguments = [\"cat\", \"/proc/sys/net/core/somaxconn\"]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let status = try await container.wait()\n            try await container.stop()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n            }\n\n            let output = String(data: buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)\n            guard output == \"4096\" else {\n                throw IntegrationError.assert(\n                    msg: \"sysctl net.core.somaxconn should be '4096', got '\\(output ?? \"nil\")'\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testSysctlMultiple() async throws {\n        let id = \"test-container-sysctl-multiple\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.sysctl = [\n                \"net.core.somaxconn\": \"2048\",\n                \"net.ipv4.ip_forward\": \"1\",\n            ]\n            config.process.arguments = [\n                \"/bin/sh\", \"-c\",\n                \"cat /proc/sys/net/core/somaxconn && cat /proc/sys/net/ipv4/ip_forward\",\n            ]\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let status = try await container.wait()\n            try await container.stop()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n            }\n\n            let output = String(data: buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)\n            let lines = output?.split(separator: \"\\n\").map { $0.trimmingCharacters(in: .whitespaces) }\n            guard lines == [\"2048\", \"1\"] else {\n                throw IntegrationError.assert(\n                    msg: \"expected sysctls ['2048', '1'], got '\\(output ?? \"nil\")'\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testNoNewPrivileges() async throws {\n        let id = \"test-no-new-privileges\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"cat\", \"/proc/self/status\"]\n            config.process.noNewPrivileges = true\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        // /proc/self/status contains \"NoNewPrivs:\\t1\" when the bit is set\n        guard output.contains(\"NoNewPrivs:\\t1\") else {\n            throw IntegrationError.assert(msg: \"expected NoNewPrivs to be 1, got: \\(output)\")\n        }\n    }\n\n    func testNoNewPrivilegesDisabled() async throws {\n        let id = \"test-no-new-privileges-disabled\"\n\n        let bs = try await bootstrap(id)\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"cat\", \"/proc/self/status\"]\n            // noNewPrivileges defaults to false\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        try await container.create()\n        try await container.start()\n\n        let status = try await container.wait()\n        try await container.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        // When noNewPrivileges is not set, NoNewPrivs should be 0\n        guard output.contains(\"NoNewPrivs:\\t0\") else {\n            throw IntegrationError.assert(msg: \"expected NoNewPrivs to be 0, got: \\(output)\")\n        }\n    }\n\n    func testWorkingDirCreated() async throws {\n        let id = \"test-working-dir-created\"\n        let bs = try await bootstrap(id)\n\n        let buffer = BufferWriter()\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/pwd\"]\n            config.process.workingDirectory = \"/does/not/exist\"\n            config.process.stdout = buffer\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let status = try await container.wait()\n            try await container.stop()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"process with non-existent workingDir failed: \\(status)\")\n            }\n\n            guard let output = String(data: buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {\n                throw IntegrationError.assert(msg: \"failed to read stdout\")\n            }\n\n            guard output == \"/does/not/exist\" else {\n                throw IntegrationError.assert(msg: \"expected cwd '/does/not/exist', got '\\(output)'\")\n            }\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testWorkingDirExecCreated() async throws {\n        let id = \"test-working-dir-exec-created\"\n        let bs = try await bootstrap(id)\n\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"/bin/sleep\", \"1000\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            let buffer = BufferWriter()\n            let exec = try await container.exec(\"cwd-exec\") { config in\n                config.arguments = [\"/bin/pwd\"]\n                config.workingDirectory = \"/a/b/c/d\"\n                config.stdout = buffer\n            }\n\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"exec with non-existent workingDir failed: \\(status)\")\n            }\n\n            guard let output = String(data: buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {\n                throw IntegrationError.assert(msg: \"failed to read stdout\")\n            }\n\n            guard output == \"/a/b/c/d\" else {\n                throw IntegrationError.assert(msg: \"expected cwd '/a/b/c/d', got '\\(output)'\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n\n    func testNoNewPrivilegesExec() async throws {\n        let id = \"test-no-new-privileges-exec\"\n\n        let bs = try await bootstrap(id)\n        let container = try LinuxContainer(id, rootfs: bs.rootfs, vmm: bs.vmm) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.bootLog = bs.bootLog\n        }\n\n        do {\n            try await container.create()\n            try await container.start()\n\n            // Exec a process with noNewPrivileges set\n            let buffer = BufferWriter()\n            let exec = try await container.exec(\"nnp-exec\") { config in\n                config.arguments = [\"cat\", \"/proc/self/status\"]\n                config.noNewPrivileges = true\n                config.stdout = buffer\n            }\n\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"exec status \\(status) != 0\")\n            }\n\n            guard let output = String(data: buffer.data, encoding: .utf8) else {\n                throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n            }\n\n            guard output.contains(\"NoNewPrivs:\\t1\") else {\n                throw IntegrationError.assert(msg: \"expected NoNewPrivs to be 1 in exec, got: \\(output)\")\n            }\n\n            try await container.kill(SIGKILL)\n            try await container.wait()\n            try await container.stop()\n        } catch {\n            try? await container.stop()\n            throw error\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/Integration/PodTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ArgumentParser\nimport Containerization\nimport ContainerizationError\nimport ContainerizationOCI\nimport ContainerizationOS\nimport Foundation\nimport Logging\n\nextension IntegrationSuite {\n    /// Clone a rootfs mount to a new location for use by a container in a pod\n    private func cloneRootfs(_ rootfs: Containerization.Mount, testID: String, containerID: String) throws -> Containerization.Mount {\n        let clonePath = Self.testDir.appending(component: \"\\(testID)-\\(containerID).ext4\").absolutePath()\n        try? FileManager.default.removeItem(atPath: clonePath)\n        return try rootfs.clone(to: clonePath)\n    }\n\n    func testPodSingleContainer() async throws {\n        let id = \"test-pod-single-container\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"/bin/true\"]\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n\n        let status = try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n    }\n\n    func testPodMultipleContainers() async throws {\n        let id = \"test-pod-multiple-containers\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"/bin/true\"]\n        }\n\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.process.arguments = [\"/bin/echo\", \"hello\"]\n        }\n\n        try await pod.create()\n\n        try await pod.startContainer(\"container1\")\n        let status1 = try await pod.waitContainer(\"container1\")\n\n        try await pod.startContainer(\"container2\")\n        let status2 = try await pod.waitContainer(\"container2\")\n\n        try await pod.stop()\n\n        guard status1.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container1 status \\(status1) != 0\")\n        }\n\n        guard status2.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container2 status \\(status2) != 0\")\n        }\n    }\n\n    func testPodContainerOutput() async throws {\n        let id = \"test-pod-container-output\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        let buffer = BufferWriter()\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"/bin/echo\", \"hello from pod\"]\n            config.process.stdout = buffer\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n\n        let status = try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard String(data: buffer.data, encoding: .utf8) == \"hello from pod\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"process should have returned on stdout 'hello from pod' != '\\(String(data: buffer.data, encoding: .utf8)!)'\")\n        }\n    }\n\n    func testPodConcurrentContainers() async throws {\n        let id = \"test-pod-concurrent-containers\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        // Add 5 containers\n        for i in 0..<5 {\n            try await pod.addContainer(\"container\\(i)\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container\\(i)\")) { config in\n                config.process.arguments = [\"/bin/sleep\", \"1\"]\n            }\n        }\n\n        try await pod.create()\n\n        // Start all containers concurrently\n        try await withThrowingTaskGroup(of: Void.self) { group in\n            for i in 0..<5 {\n                group.addTask {\n                    try await pod.startContainer(\"container\\(i)\")\n                }\n            }\n            try await group.waitForAll()\n        }\n\n        // Wait for all containers concurrently\n        try await withThrowingTaskGroup(of: Void.self) { group in\n            for i in 0..<5 {\n                group.addTask {\n                    let status = try await pod.waitContainer(\"container\\(i)\")\n                    if status.exitCode != 0 {\n                        throw IntegrationError.assert(msg: \"container\\(i) status \\(status) != 0\")\n                    }\n                }\n            }\n            try await group.waitForAll()\n        }\n\n        try await pod.stop()\n    }\n\n    func testPodExecInContainer() async throws {\n        let id = \"test-pod-exec-in-container\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"/bin/sleep\", \"100\"]\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n\n        let buffer = BufferWriter()\n        let exec = try await pod.execInContainer(\"container1\", processID: \"exec1\") { config in\n            config.arguments = [\"/bin/echo\", \"exec test\"]\n            config.stdout = buffer\n        }\n\n        try await exec.start()\n        let status = try await exec.wait()\n        try await exec.delete()\n\n        try await pod.killContainer(\"container1\", signal: SIGKILL)\n        try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"exec status \\(status) != 0\")\n        }\n\n        guard String(data: buffer.data, encoding: .utf8) == \"exec test\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"exec should have returned 'exec test' != '\\(String(data: buffer.data, encoding: .utf8)!)'\")\n        }\n    }\n\n    func testPodExecInContainerEnv() async throws {\n        let id = \"test-pod-exec-in-container-env\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"/bin/sleep\", \"100\"]\n            config.process.environmentVariables.append(\"MY_VAR=hello_from_container\")\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n\n        let buffer = BufferWriter()\n        let exec = try await pod.execInContainer(\"container1\", processID: \"exec1\") { config in\n            config.arguments = [\"/bin/sh\", \"-c\", \"printenv MY_VAR\"]\n            config.stdout = buffer\n        }\n\n        try await exec.start()\n        let status = try await exec.wait()\n        try await exec.delete()\n\n        try await pod.killContainer(\"container1\", signal: SIGKILL)\n        try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"exec env status \\(status) != 0\")\n        }\n\n        guard String(data: buffer.data, encoding: .utf8) == \"hello_from_container\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"exec should have inherited container env MY_VAR=hello_from_container, got '\\(String(data: buffer.data, encoding: .utf8) ?? \"nil\")'\")\n        }\n    }\n\n    func testPodContainerHostname() async throws {\n        let id = \"test-pod-container-hostname\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        let buffer = BufferWriter()\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"/bin/hostname\"]\n            config.hostname = \"my-pod-container\"\n            config.process.stdout = buffer\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n\n        let status = try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard String(data: buffer.data, encoding: .utf8) == \"my-pod-container\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"hostname should be 'my-pod-container' != '\\(String(data: buffer.data, encoding: .utf8)!)'\")\n        }\n    }\n\n    func testPodContainerHostnameDefaultsToContainerID() async throws {\n        let id = \"test-pod-container-hostname-default\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        let buffer = BufferWriter()\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"/bin/hostname\"]\n            config.process.stdout = buffer\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n\n        let status = try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard String(data: buffer.data, encoding: .utf8) == \"container1\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"hostname should default to container id 'container1', got '\\(String(data: buffer.data, encoding: .utf8)!)'\")\n        }\n    }\n\n    func testPodStopContainerIdempotency() async throws {\n        let id = \"test-pod-stop-container-idempotency\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"/bin/true\"]\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n\n        let status = try await pod.waitContainer(\"container1\")\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        // Stop container twice - should not fail\n        try await pod.stopContainer(\"container1\")\n        try await pod.stopContainer(\"container1\")\n\n        try await pod.stop()\n    }\n\n    func testPodListContainers() async throws {\n        let id = \"test-pod-list-containers\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        let containerIDs = [\"container1\", \"container2\", \"container3\"]\n        for containerID in containerIDs {\n            try await pod.addContainer(containerID, rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: containerID)) { config in\n                config.process.arguments = [\"/bin/true\"]\n            }\n        }\n\n        let listedContainers = await pod.listContainers()\n\n        guard Set(listedContainers) == Set(containerIDs) else {\n            throw IntegrationError.assert(\n                msg: \"listed containers \\(listedContainers) != expected \\(containerIDs)\")\n        }\n\n        try await pod.create()\n        try await pod.stop()\n    }\n\n    func testPodContainerStatistics() async throws {\n        let id = \"test-pod-container-statistics\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"/bin/sleep\", \"infinity\"]\n        }\n\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.process.arguments = [\"/bin/sleep\", \"infinity\"]\n        }\n\n        do {\n            try await pod.create()\n            try await pod.startContainer(\"container1\")\n            try await pod.startContainer(\"container2\")\n\n            let stats = try await pod.statistics()\n\n            guard stats.count == 2 else {\n                throw IntegrationError.assert(msg: \"expected 2 container stats, got \\(stats.count)\")\n            }\n\n            let containerIDs = Set(stats.map { $0.id })\n            guard containerIDs == Set([\"container1\", \"container2\"]) else {\n                throw IntegrationError.assert(msg: \"unexpected container IDs in stats: \\(containerIDs)\")\n            }\n\n            for stat in stats {\n                guard let process = stat.process, process.current > 0 else {\n                    throw IntegrationError.assert(msg: \"container \\(stat.id) process count should be > 0\")\n                }\n\n                guard let memory = stat.memory, memory.usageBytes > 0 else {\n                    throw IntegrationError.assert(msg: \"container \\(stat.id) memory usage should be > 0\")\n                }\n\n                print(\"Container \\(stat.id) statistics:\")\n                print(\"  Processes: \\(process.current)\")\n                print(\"  Memory: \\(memory.usageBytes) bytes\")\n                print(\"  CPU: \\(stat.cpu?.usageUsec ?? 0) usec\")\n            }\n\n            try await pod.stop()\n        } catch {\n            try? await pod.stop()\n            throw error\n        }\n    }\n\n    func testPodMemoryEventsOOMKill() async throws {\n        let id = \"test-pod-memory-events-oom-kill\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"/bin/sleep\", \"infinity\"]\n        }\n\n        do {\n            try await pod.create()\n            try await pod.startContainer(\"container1\")\n\n            let exec = try await pod.execInContainer(\"container1\", processID: \"oom-trigger\") { config in\n                config.arguments = [\n                    \"sh\",\n                    \"-c\",\n                    \"echo 2097152 > /sys/fs/cgroup/memory.max && dd if=/dev/zero of=/dev/null bs=100M\",\n                ]\n            }\n\n            try await exec.start()\n            let status = try await exec.wait()\n            if status.exitCode == 0 {\n                throw IntegrationError.assert(msg: \"expected exit code > 0\")\n            }\n            try await exec.delete()\n\n            let stats = try await pod.statistics(containerIDs: [\"container1\"], categories: .memoryEvents)\n\n            guard let containerStats = stats.first, let events = containerStats.memoryEvents else {\n                throw IntegrationError.assert(msg: \"expected memoryEvents to be present\")\n            }\n\n            print(\"Memory events for pod container container1:\")\n            print(\"  low: \\(events.low)\")\n            print(\"  high: \\(events.high)\")\n            print(\"  max: \\(events.max)\")\n            print(\"  oom: \\(events.oom)\")\n            print(\"  oomKill: \\(events.oomKill)\")\n\n            guard events.oomKill > 0 else {\n                throw IntegrationError.assert(msg: \"expected oomKill > 0, got \\(events.oomKill)\")\n            }\n\n            try await pod.killContainer(\"container1\", signal: SIGKILL)\n            try await pod.waitContainer(\"container1\")\n            try await pod.stop()\n        } catch {\n            try? await pod.stop()\n            throw error\n        }\n    }\n\n    func testPodContainerResourceLimits() async throws {\n        let id = \"test-pod-container-resource-limits\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"/bin/sleep\", \"infinity\"]\n            config.cpus = 2\n            config.memoryInBytes = 256.mib()\n        }\n\n        do {\n            try await pod.create()\n            try await pod.startContainer(\"container1\")\n\n            // Verify memory limit\n            let memoryBuffer = BufferWriter()\n            let memoryExec = try await pod.execInContainer(\"container1\", processID: \"check-memory\") { config in\n                config.arguments = [\"cat\", \"/sys/fs/cgroup/memory.max\"]\n                config.stdout = memoryBuffer\n            }\n            try await memoryExec.start()\n            var status = try await memoryExec.wait()\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"check-memory status \\(status) != 0\")\n            }\n            try await memoryExec.delete()\n\n            guard let memoryLimit = String(data: memoryBuffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {\n                throw IntegrationError.assert(msg: \"failed to parse memory.max\")\n            }\n            let expectedMemory = \"\\(256.mib())\"\n            guard memoryLimit == expectedMemory else {\n                throw IntegrationError.assert(msg: \"memory.max \\(memoryLimit) != expected \\(expectedMemory)\")\n            }\n\n            // Verify CPU limit\n            let cpuBuffer = BufferWriter()\n            let cpuExec = try await pod.execInContainer(\"container1\", processID: \"check-cpu\") { config in\n                config.arguments = [\"cat\", \"/sys/fs/cgroup/cpu.max\"]\n                config.stdout = cpuBuffer\n            }\n            try await cpuExec.start()\n            status = try await cpuExec.wait()\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"check-cpu status \\(status) != 0\")\n            }\n            try await cpuExec.delete()\n\n            guard let cpuLimit = String(data: cpuBuffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {\n                throw IntegrationError.assert(msg: \"failed to parse cpu.max\")\n            }\n            let expectedCpu = \"200000 100000\"  // 2 CPUs: quota=200000, period=100000\n            guard cpuLimit == expectedCpu else {\n                throw IntegrationError.assert(msg: \"cpu.max '\\(cpuLimit)' != expected '\\(expectedCpu)'\")\n            }\n\n            try await pod.killContainer(\"container1\", signal: SIGKILL)\n            try await pod.waitContainer(\"container1\")\n            try await pod.stop()\n        } catch {\n            try? await pod.stop()\n            throw error\n        }\n    }\n\n    func testPodContainerFilesystemIsolation() async throws {\n        let id = \"test-pod-container-filesystem-isolation\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"/bin/sleep\", \"infinity\"]\n        }\n\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.process.arguments = [\"/bin/sleep\", \"infinity\"]\n        }\n\n        do {\n            try await pod.create()\n            try await pod.startContainer(\"container1\")\n            try await pod.startContainer(\"container2\")\n\n            // Write a file in container1\n            let writeExec = try await pod.execInContainer(\"container1\", processID: \"write-file\") { config in\n                config.arguments = [\"sh\", \"-c\", \"echo 'secret data' > /tmp/container1-secret.txt\"]\n            }\n            try await writeExec.start()\n            var status = try await writeExec.wait()\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"write-file status \\(status) != 0\")\n            }\n            try await writeExec.delete()\n\n            // Verify the file exists in container1\n            let readBuffer1 = BufferWriter()\n            let readExec1 = try await pod.execInContainer(\"container1\", processID: \"read-file-1\") { config in\n                config.arguments = [\"cat\", \"/tmp/container1-secret.txt\"]\n                config.stdout = readBuffer1\n            }\n            try await readExec1.start()\n            status = try await readExec1.wait()\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"read-file-1 status \\(status) != 0\")\n            }\n            try await readExec1.delete()\n\n            guard String(data: readBuffer1.data, encoding: .utf8) == \"secret data\\n\" else {\n                throw IntegrationError.assert(msg: \"file content in container1 should be 'secret data'\")\n            }\n\n            // Try to read the file from container2 - should fail\n            let readExec2 = try await pod.execInContainer(\"container2\", processID: \"read-file-2\") { config in\n                config.arguments = [\"cat\", \"/tmp/container1-secret.txt\"]\n            }\n            try await readExec2.start()\n            status = try await readExec2.wait()\n            try await readExec2.delete()\n\n            // File should NOT exist in container2, so cat should fail\n            guard status.exitCode != 0 else {\n                throw IntegrationError.assert(msg: \"file should NOT be accessible from container2\")\n            }\n\n            try await pod.stop()\n        } catch {\n            try? await pod.stop()\n            throw error\n        }\n    }\n\n    func testPodContainerPIDNamespaceIsolation() async throws {\n        let id = \"test-pod-container-pid-isolation\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"/bin/sleep\", \"infinity\"]\n        }\n\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.process.arguments = [\"/bin/sleep\", \"infinity\"]\n        }\n\n        do {\n            try await pod.create()\n            try await pod.startContainer(\"container1\")\n            try await pod.startContainer(\"container2\")\n\n            // Start a unique process in container1\n            let sleepExec1 = try await pod.execInContainer(\"container1\", processID: \"unique-sleep-1\") { config in\n                config.arguments = [\"/bin/sleep\", \"9999\"]\n            }\n            try await sleepExec1.start()\n\n            // List processes in container1 - should see sleep 9999\n            let ps1Buffer = BufferWriter()\n            let psExec1 = try await pod.execInContainer(\"container1\", processID: \"ps-1\") { config in\n                config.arguments = [\"ps\", \"aux\"]\n                config.stdout = ps1Buffer\n            }\n            try await psExec1.start()\n            var status = try await psExec1.wait()\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"ps-1 status \\(status) != 0\")\n            }\n            try await psExec1.delete()\n\n            guard let ps1Output = String(data: ps1Buffer.data, encoding: .utf8) else {\n                throw IntegrationError.assert(msg: \"failed to parse ps output from container1\")\n            }\n\n            // Verify sleep 9999 is visible in container1\n            guard ps1Output.contains(\"sleep 9999\") else {\n                throw IntegrationError.assert(msg: \"sleep 9999 should be visible in container1\")\n            }\n\n            // List processes in container2 - should NOT see sleep 9999\n            let ps2Buffer = BufferWriter()\n            let psExec2 = try await pod.execInContainer(\"container2\", processID: \"ps-2\") { config in\n                config.arguments = [\"ps\", \"aux\"]\n                config.stdout = ps2Buffer\n            }\n            try await psExec2.start()\n            status = try await psExec2.wait()\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"ps-2 status \\(status) != 0\")\n            }\n            try await psExec2.delete()\n\n            guard let ps2Output = String(data: ps2Buffer.data, encoding: .utf8) else {\n                throw IntegrationError.assert(msg: \"failed to parse ps output from container2\")\n            }\n\n            // Verify sleep 9999 is NOT visible in container2\n            guard !ps2Output.contains(\"sleep 9999\") else {\n                throw IntegrationError.assert(msg: \"sleep 9999 should NOT be visible in container2 (PID namespace isolation failed)\")\n            }\n\n            try await sleepExec1.delete()\n            try await pod.stop()\n        } catch {\n            try? await pod.stop()\n            throw error\n        }\n    }\n\n    func testPodContainerIndependentResourceLimits() async throws {\n        let id = \"test-pod-container-independent-limits\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        // Container1 with 1 CPU and 128 MiB memory\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"/bin/sleep\", \"infinity\"]\n            config.cpus = 1\n            config.memoryInBytes = 128.mib()\n        }\n\n        // Container2 with 2 CPUs and 256 MiB memory\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.process.arguments = [\"/bin/sleep\", \"infinity\"]\n            config.cpus = 2\n            config.memoryInBytes = 256.mib()\n        }\n\n        do {\n            try await pod.create()\n            try await pod.startContainer(\"container1\")\n            try await pod.startContainer(\"container2\")\n\n            // Verify container1 memory limit\n            let mem1Buffer = BufferWriter()\n            let memExec1 = try await pod.execInContainer(\"container1\", processID: \"check-mem-1\") { config in\n                config.arguments = [\"cat\", \"/sys/fs/cgroup/memory.max\"]\n                config.stdout = mem1Buffer\n            }\n            try await memExec1.start()\n            var status = try await memExec1.wait()\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"check-mem-1 status \\(status) != 0\")\n            }\n            try await memExec1.delete()\n\n            guard let mem1Limit = String(data: mem1Buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {\n                throw IntegrationError.assert(msg: \"failed to parse memory.max from container1\")\n            }\n\n            let expectedMem1 = \"\\(128.mib())\"\n            guard mem1Limit == expectedMem1 else {\n                throw IntegrationError.assert(msg: \"container1 memory.max \\(mem1Limit) != expected \\(expectedMem1)\")\n            }\n\n            // Verify container1 CPU limit\n            let cpu1Buffer = BufferWriter()\n            let cpuExec1 = try await pod.execInContainer(\"container1\", processID: \"check-cpu-1\") { config in\n                config.arguments = [\"cat\", \"/sys/fs/cgroup/cpu.max\"]\n                config.stdout = cpu1Buffer\n            }\n            try await cpuExec1.start()\n            status = try await cpuExec1.wait()\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"check-cpu-1 status \\(status) != 0\")\n            }\n            try await cpuExec1.delete()\n\n            guard let cpu1Limit = String(data: cpu1Buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {\n                throw IntegrationError.assert(msg: \"failed to parse cpu.max from container1\")\n            }\n\n            let expectedCpu1 = \"100000 100000\"  // 1 CPU\n            guard cpu1Limit == expectedCpu1 else {\n                throw IntegrationError.assert(msg: \"container1 cpu.max '\\(cpu1Limit)' != expected '\\(expectedCpu1)'\")\n            }\n\n            // Verify container2 memory limit\n            let mem2Buffer = BufferWriter()\n            let memExec2 = try await pod.execInContainer(\"container2\", processID: \"check-mem-2\") { config in\n                config.arguments = [\"cat\", \"/sys/fs/cgroup/memory.max\"]\n                config.stdout = mem2Buffer\n            }\n            try await memExec2.start()\n            status = try await memExec2.wait()\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"check-mem-2 status \\(status) != 0\")\n            }\n            try await memExec2.delete()\n\n            guard let mem2Limit = String(data: mem2Buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {\n                throw IntegrationError.assert(msg: \"failed to parse memory.max from container2\")\n            }\n\n            let expectedMem2 = \"\\(256.mib())\"\n            guard mem2Limit == expectedMem2 else {\n                throw IntegrationError.assert(msg: \"container2 memory.max \\(mem2Limit) != expected \\(expectedMem2)\")\n            }\n\n            // Verify container2 CPU limit\n            let cpu2Buffer = BufferWriter()\n            let cpuExec2 = try await pod.execInContainer(\"container2\", processID: \"check-cpu-2\") { config in\n                config.arguments = [\"cat\", \"/sys/fs/cgroup/cpu.max\"]\n                config.stdout = cpu2Buffer\n            }\n            try await cpuExec2.start()\n            status = try await cpuExec2.wait()\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"check-cpu-2 status \\(status) != 0\")\n            }\n            try await cpuExec2.delete()\n\n            guard let cpu2Limit = String(data: cpu2Buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {\n                throw IntegrationError.assert(msg: \"failed to parse cpu.max from container2\")\n            }\n\n            let expectedCpu2 = \"200000 100000\"  // 2 CPUs\n            guard cpu2Limit == expectedCpu2 else {\n                throw IntegrationError.assert(msg: \"container2 cpu.max '\\(cpu2Limit)' != expected '\\(expectedCpu2)'\")\n            }\n\n            try await pod.stop()\n        } catch {\n            try? await pod.stop()\n            throw error\n        }\n    }\n\n    func testPodSharedPIDNamespace() async throws {\n        let id = \"test-pod-shared-pid-namespace\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n            config.shareProcessNamespace = true\n        }\n\n        // First container runs a long-running process\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"/bin/sleep\", \"300\"]\n        }\n\n        // Second container checks if it can see container1's sleep process\n        let psBuffer = BufferWriter()\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.process.arguments = [\"/bin/sh\", \"-c\", \"ps aux | grep 'sleep 300' | grep -v grep\"]\n            config.process.stdout = psBuffer\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n        try await Task.sleep(for: .milliseconds(100))\n\n        try await pod.startContainer(\"container2\")\n        let status = try await pod.waitContainer(\"container2\")\n\n        try await pod.killContainer(\"container1\", signal: SIGKILL)\n        _ = try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container2 should have found the sleep process (status: \\(status))\")\n        }\n\n        let output = String(data: psBuffer.data, encoding: .utf8) ?? \"\"\n        guard output.contains(\"sleep 300\") else {\n            throw IntegrationError.assert(msg: \"ps output should contain 'sleep 300', got: '\\(output)'\")\n        }\n    }\n\n    func testPodReadOnlyRootfs() async throws {\n        let id = \"test-pod-readonly-rootfs\"\n\n        let bs = try await bootstrap(id)\n        var rootfs = bs.rootfs\n        rootfs.options.append(\"ro\")\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: rootfs) { config in\n            config.process.arguments = [\"touch\", \"/testfile\"]\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n\n        let status = try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        // touch should fail on a read-only rootfs\n        guard status.exitCode != 0 else {\n            throw IntegrationError.assert(msg: \"touch should have failed on read-only rootfs\")\n        }\n    }\n\n    func testPodReadOnlyRootfsDNSConfigured() async throws {\n        let id = \"test-pod-readonly-rootfs-dns\"\n\n        let bs = try await bootstrap(id)\n        var rootfs = bs.rootfs\n        rootfs.options.append(\"ro\")\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        let buffer = BufferWriter()\n        try await pod.addContainer(\"container1\", rootfs: rootfs) { config in\n            // Verify /etc/resolv.conf was written before rootfs was remounted read-only\n            config.process.arguments = [\"cat\", \"/etc/resolv.conf\"]\n            config.process.stdout = buffer\n            config.dns = DNS(nameservers: [\"8.8.8.8\", \"8.8.4.4\"])\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n\n        let status = try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"cat /etc/resolv.conf failed with status \\(status)\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        guard output.contains(\"8.8.8.8\") && output.contains(\"8.8.4.4\") else {\n            throw IntegrationError.assert(msg: \"expected /etc/resolv.conf to contain DNS servers, got: \\(output)\")\n        }\n    }\n\n    func testPodSingleFileMount() async throws {\n        let id = \"test-pod-single-file-mount\"\n\n        let bs = try await bootstrap(id)\n\n        // Create a temp file with known content\n        let testContent = \"Hello from pod single file mount!\"\n        let hostFile = FileManager.default.uniqueTemporaryDirectory(create: true)\n            .appendingPathComponent(\"pod-config.txt\")\n        try testContent.write(to: hostFile, atomically: true, encoding: .utf8)\n\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        let buffer = BufferWriter()\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"cat\", \"/etc/myconfig.txt\"]\n            // Mount a single file using virtiofs share\n            config.mounts.append(.share(source: hostFile.path, destination: \"/etc/myconfig.txt\"))\n            config.process.stdout = buffer\n        }\n\n        do {\n            try await pod.create()\n            try await pod.startContainer(\"container1\")\n\n            let status = try await pod.waitContainer(\"container1\")\n            try await pod.stop()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n            }\n\n            guard let output = String(data: buffer.data, encoding: .utf8) else {\n                throw IntegrationError.assert(msg: \"failed to convert output to UTF8\")\n            }\n\n            guard output == testContent else {\n                throw IntegrationError.assert(\n                    msg: \"expected '\\(testContent)', got '\\(output)'\")\n            }\n        } catch {\n            try? await pod.stop()\n            throw error\n        }\n    }\n\n    func testPodContainerHostsConfig() async throws {\n        let id = \"test-pod-container-hosts\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        let buffer = BufferWriter()\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"cat\", \"/etc/hosts\"]\n            config.process.stdout = buffer\n            config.hosts = Hosts(entries: [\n                Hosts.Entry.localHostIPV4(),\n                Hosts.Entry.localHostIPV6(),\n                Hosts.Entry(ipAddress: \"10.0.0.50\", hostnames: [\"myservice.local\", \"myservice\"]),\n            ])\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n\n        let status = try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"cat /etc/hosts failed with status \\(status)\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        guard output.contains(\"10.0.0.50\") && output.contains(\"myservice.local\") else {\n            throw IntegrationError.assert(msg: \"expected /etc/hosts to contain custom entry, got: \\(output)\")\n        }\n    }\n\n    func testPodMultipleContainersDifferentDNS() async throws {\n        let id = \"test-pod-multi-dns\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        let buffer1 = BufferWriter()\n        let buffer2 = BufferWriter()\n\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"cat\", \"/etc/resolv.conf\"]\n            config.process.stdout = buffer1\n            config.dns = DNS(nameservers: [\"1.1.1.1\"])\n        }\n\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.process.arguments = [\"cat\", \"/etc/resolv.conf\"]\n            config.process.stdout = buffer2\n            config.dns = DNS(nameservers: [\"8.8.8.8\"])\n        }\n\n        try await pod.create()\n\n        try await pod.startContainer(\"container1\")\n        let status1 = try await pod.waitContainer(\"container1\")\n\n        try await pod.startContainer(\"container2\")\n        let status2 = try await pod.waitContainer(\"container2\")\n\n        try await pod.stop()\n\n        guard status1.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container1 cat failed with status \\(status1)\")\n        }\n        guard status2.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container2 cat failed with status \\(status2)\")\n        }\n\n        guard let output1 = String(data: buffer1.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert container1 stdout to UTF8\")\n        }\n        guard let output2 = String(data: buffer2.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert container2 stdout to UTF8\")\n        }\n\n        guard output1.contains(\"1.1.1.1\") && !output1.contains(\"8.8.8.8\") else {\n            throw IntegrationError.assert(msg: \"container1 should have 1.1.1.1 DNS, got: \\(output1)\")\n        }\n        guard output2.contains(\"8.8.8.8\") && !output2.contains(\"1.1.1.1\") else {\n            throw IntegrationError.assert(msg: \"container2 should have 8.8.8.8 DNS, got: \\(output2)\")\n        }\n    }\n\n    func testPodMultipleContainersDifferentHosts() async throws {\n        let id = \"test-pod-multi-hosts\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        let buffer1 = BufferWriter()\n        let buffer2 = BufferWriter()\n\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"cat\", \"/etc/hosts\"]\n            config.process.stdout = buffer1\n            config.hosts = Hosts(entries: [\n                Hosts.Entry.localHostIPV4(),\n                Hosts.Entry(ipAddress: \"10.0.0.1\", hostnames: [\"service-a.local\", \"service-a\"]),\n            ])\n        }\n\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.process.arguments = [\"cat\", \"/etc/hosts\"]\n            config.process.stdout = buffer2\n            config.hosts = Hosts(entries: [\n                Hosts.Entry.localHostIPV4(),\n                Hosts.Entry(ipAddress: \"10.0.0.2\", hostnames: [\"service-b.local\", \"service-b\"]),\n            ])\n        }\n\n        try await pod.create()\n\n        try await pod.startContainer(\"container1\")\n        let status1 = try await pod.waitContainer(\"container1\")\n\n        try await pod.startContainer(\"container2\")\n        let status2 = try await pod.waitContainer(\"container2\")\n\n        try await pod.stop()\n\n        guard status1.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container1 cat failed with status \\(status1)\")\n        }\n        guard status2.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container2 cat failed with status \\(status2)\")\n        }\n\n        guard let output1 = String(data: buffer1.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert container1 stdout to UTF8\")\n        }\n        guard let output2 = String(data: buffer2.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert container2 stdout to UTF8\")\n        }\n\n        guard output1.contains(\"10.0.0.1\") && output1.contains(\"service-a.local\") else {\n            throw IntegrationError.assert(msg: \"container1 should have service-a entry, got: \\(output1)\")\n        }\n        guard !output1.contains(\"10.0.0.2\") && !output1.contains(\"service-b\") else {\n            throw IntegrationError.assert(msg: \"container1 should NOT have service-b entry, got: \\(output1)\")\n        }\n\n        guard output2.contains(\"10.0.0.2\") && output2.contains(\"service-b.local\") else {\n            throw IntegrationError.assert(msg: \"container2 should have service-b entry, got: \\(output2)\")\n        }\n        guard !output2.contains(\"10.0.0.1\") && !output2.contains(\"service-a\") else {\n            throw IntegrationError.assert(msg: \"container2 should NOT have service-a entry, got: \\(output2)\")\n        }\n    }\n\n    func testPodLevelDNS() async throws {\n        let id = \"test-pod-level-dns\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n            // Set DNS at the pod level\n            config.dns = DNS(nameservers: [\"9.9.9.9\", \"149.112.112.112\"])\n        }\n\n        let buffer1 = BufferWriter()\n        let buffer2 = BufferWriter()\n\n        // Neither container specifies DNS. We should inherit from pod\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"cat\", \"/etc/resolv.conf\"]\n            config.process.stdout = buffer1\n        }\n\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.process.arguments = [\"cat\", \"/etc/resolv.conf\"]\n            config.process.stdout = buffer2\n        }\n\n        try await pod.create()\n\n        try await pod.startContainer(\"container1\")\n        let status1 = try await pod.waitContainer(\"container1\")\n\n        try await pod.startContainer(\"container2\")\n        let status2 = try await pod.waitContainer(\"container2\")\n\n        try await pod.stop()\n\n        guard status1.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container1 cat failed with status \\(status1)\")\n        }\n        guard status2.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container2 cat failed with status \\(status2)\")\n        }\n\n        guard let output1 = String(data: buffer1.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert container1 stdout to UTF8\")\n        }\n        guard let output2 = String(data: buffer2.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert container2 stdout to UTF8\")\n        }\n\n        // Both containers should have the pod-level DNS\n        guard output1.contains(\"9.9.9.9\") && output1.contains(\"149.112.112.112\") else {\n            throw IntegrationError.assert(msg: \"container1 should have pod-level DNS (9.9.9.9), got: \\(output1)\")\n        }\n        guard output2.contains(\"9.9.9.9\") && output2.contains(\"149.112.112.112\") else {\n            throw IntegrationError.assert(msg: \"container2 should have pod-level DNS (9.9.9.9), got: \\(output2)\")\n        }\n    }\n\n    func testPodLevelDNSWithContainerOverride() async throws {\n        let id = \"test-pod-level-dns-override\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n            // Set DNS at the pod level\n            config.dns = DNS(nameservers: [\"9.9.9.9\"])\n        }\n\n        let buffer1 = BufferWriter()\n        let buffer2 = BufferWriter()\n\n        // Container1 does NOT specify DNS. It should inherit from pod\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"cat\", \"/etc/resolv.conf\"]\n            config.process.stdout = buffer1\n        }\n\n        // Container2 specifies its own DNS. It should override pod-level\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.process.arguments = [\"cat\", \"/etc/resolv.conf\"]\n            config.process.stdout = buffer2\n            config.dns = DNS(nameservers: [\"8.8.8.8\"])\n        }\n\n        try await pod.create()\n\n        try await pod.startContainer(\"container1\")\n        let status1 = try await pod.waitContainer(\"container1\")\n\n        try await pod.startContainer(\"container2\")\n        let status2 = try await pod.waitContainer(\"container2\")\n\n        try await pod.stop()\n\n        guard status1.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container1 cat failed with status \\(status1)\")\n        }\n        guard status2.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container2 cat failed with status \\(status2)\")\n        }\n\n        guard let output1 = String(data: buffer1.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert container1 stdout to UTF8\")\n        }\n        guard let output2 = String(data: buffer2.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert container2 stdout to UTF8\")\n        }\n\n        // Container1 should have pod-level DNS\n        guard output1.contains(\"9.9.9.9\") && !output1.contains(\"8.8.8.8\") else {\n            throw IntegrationError.assert(msg: \"container1 should have pod-level DNS (9.9.9.9), got: \\(output1)\")\n        }\n        // Container2 should have its own DNS, not pod-level\n        guard output2.contains(\"8.8.8.8\") && !output2.contains(\"9.9.9.9\") else {\n            throw IntegrationError.assert(msg: \"container2 should have container-level DNS (8.8.8.8), got: \\(output2)\")\n        }\n    }\n\n    func testPodLevelHosts() async throws {\n        let id = \"test-pod-level-hosts\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n            // Set hosts at the pod level\n            config.hosts = Hosts(entries: [\n                Hosts.Entry.localHostIPV4(),\n                Hosts.Entry(ipAddress: \"10.0.0.100\", hostnames: [\"shared-service.local\"]),\n            ])\n        }\n\n        let buffer1 = BufferWriter()\n        let buffer2 = BufferWriter()\n\n        // Neither container specifies hosts. It should inherit from pod\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"cat\", \"/etc/hosts\"]\n            config.process.stdout = buffer1\n        }\n\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.process.arguments = [\"cat\", \"/etc/hosts\"]\n            config.process.stdout = buffer2\n        }\n\n        try await pod.create()\n\n        try await pod.startContainer(\"container1\")\n        let status1 = try await pod.waitContainer(\"container1\")\n\n        try await pod.startContainer(\"container2\")\n        let status2 = try await pod.waitContainer(\"container2\")\n\n        try await pod.stop()\n\n        guard status1.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container1 cat failed with status \\(status1)\")\n        }\n        guard status2.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container2 cat failed with status \\(status2)\")\n        }\n\n        guard let output1 = String(data: buffer1.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert container1 stdout to UTF8\")\n        }\n        guard let output2 = String(data: buffer2.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert container2 stdout to UTF8\")\n        }\n\n        // Both containers should have the pod-level hosts entry\n        guard output1.contains(\"10.0.0.100\") && output1.contains(\"shared-service.local\") else {\n            throw IntegrationError.assert(msg: \"container1 should have pod-level hosts entry, got: \\(output1)\")\n        }\n        guard output2.contains(\"10.0.0.100\") && output2.contains(\"shared-service.local\") else {\n            throw IntegrationError.assert(msg: \"container2 should have pod-level hosts entry, got: \\(output2)\")\n        }\n    }\n\n    func testPodLevelHostsWithContainerOverride() async throws {\n        let id = \"test-pod-level-hosts-override\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n            // Set hosts at the pod level\n            config.hosts = Hosts(entries: [\n                Hosts.Entry.localHostIPV4(),\n                Hosts.Entry(ipAddress: \"10.0.0.100\", hostnames: [\"shared-service.local\"]),\n            ])\n        }\n\n        let buffer1 = BufferWriter()\n        let buffer2 = BufferWriter()\n\n        // Container1 does NOT specify hosts. It should inherit from pod\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"cat\", \"/etc/hosts\"]\n            config.process.stdout = buffer1\n        }\n\n        // Container2 specifies its own hosts. It should override pod-level\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.process.arguments = [\"cat\", \"/etc/hosts\"]\n            config.process.stdout = buffer2\n            config.hosts = Hosts(entries: [\n                Hosts.Entry.localHostIPV4(),\n                Hosts.Entry(ipAddress: \"10.0.0.200\", hostnames: [\"container-specific.local\"]),\n            ])\n        }\n\n        try await pod.create()\n\n        try await pod.startContainer(\"container1\")\n        let status1 = try await pod.waitContainer(\"container1\")\n\n        try await pod.startContainer(\"container2\")\n        let status2 = try await pod.waitContainer(\"container2\")\n\n        try await pod.stop()\n\n        guard status1.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container1 cat failed with status \\(status1)\")\n        }\n        guard status2.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container2 cat failed with status \\(status2)\")\n        }\n\n        guard let output1 = String(data: buffer1.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert container1 stdout to UTF8\")\n        }\n        guard let output2 = String(data: buffer2.data, encoding: .utf8) else {\n            throw IntegrationError.assert(msg: \"failed to convert container2 stdout to UTF8\")\n        }\n\n        // Container1 should have pod-level hosts entry\n        guard output1.contains(\"10.0.0.100\") && output1.contains(\"shared-service.local\") else {\n            throw IntegrationError.assert(msg: \"container1 should have pod-level hosts entry, got: \\(output1)\")\n        }\n        guard !output1.contains(\"10.0.0.200\") && !output1.contains(\"container-specific.local\") else {\n            throw IntegrationError.assert(msg: \"container1 should NOT have container2's hosts entry, got: \\(output1)\")\n        }\n\n        // Container2 should have its own hosts entry, not pod-level\n        guard output2.contains(\"10.0.0.200\") && output2.contains(\"container-specific.local\") else {\n            throw IntegrationError.assert(msg: \"container2 should have container-level hosts entry, got: \\(output2)\")\n        }\n        guard !output2.contains(\"10.0.0.100\") && !output2.contains(\"shared-service.local\") else {\n            throw IntegrationError.assert(msg: \"container2 should NOT have pod-level hosts entry, got: \\(output2)\")\n        }\n    }\n\n    func testPodLevelHostname() async throws {\n        let id = \"test-pod-level-hostname\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n            // Set hostname at the pod level\n            config.hostname = \"pod-host\"\n        }\n\n        let buffer1 = BufferWriter()\n        let buffer2 = BufferWriter()\n\n        // Neither container specifies a hostname. Both should inherit from pod.\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"/bin/hostname\"]\n            config.process.stdout = buffer1\n        }\n\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.process.arguments = [\"/bin/hostname\"]\n            config.process.stdout = buffer2\n        }\n\n        try await pod.create()\n\n        try await pod.startContainer(\"container1\")\n        let status1 = try await pod.waitContainer(\"container1\")\n\n        try await pod.startContainer(\"container2\")\n        let status2 = try await pod.waitContainer(\"container2\")\n\n        try await pod.stop()\n\n        guard status1.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container1 hostname failed with status \\(status1)\")\n        }\n        guard status2.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container2 hostname failed with status \\(status2)\")\n        }\n\n        guard String(data: buffer1.data, encoding: .utf8) == \"pod-host\\n\" else {\n            throw IntegrationError.assert(msg: \"container1 should have pod-level hostname 'pod-host', got: '\\(String(data: buffer1.data, encoding: .utf8) ?? \"nil\")'\")\n        }\n        guard String(data: buffer2.data, encoding: .utf8) == \"pod-host\\n\" else {\n            throw IntegrationError.assert(msg: \"container2 should have pod-level hostname 'pod-host', got: '\\(String(data: buffer2.data, encoding: .utf8) ?? \"nil\")'\")\n        }\n    }\n\n    func testPodLevelHostnameWithContainerOverride() async throws {\n        let id = \"test-pod-level-hostname-override\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n            // Set hostname at the pod level\n            config.hostname = \"pod-host\"\n        }\n\n        let buffer1 = BufferWriter()\n        let buffer2 = BufferWriter()\n\n        // Container1 does NOT specify a hostname. It should inherit from pod.\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"/bin/hostname\"]\n            config.process.stdout = buffer1\n        }\n\n        // Container2 specifies its own hostname. It should override the pod-level value.\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.process.arguments = [\"/bin/hostname\"]\n            config.process.stdout = buffer2\n            config.hostname = \"container-host\"\n        }\n\n        try await pod.create()\n\n        try await pod.startContainer(\"container1\")\n        let status1 = try await pod.waitContainer(\"container1\")\n\n        try await pod.startContainer(\"container2\")\n        let status2 = try await pod.waitContainer(\"container2\")\n\n        try await pod.stop()\n\n        guard status1.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container1 hostname failed with status \\(status1)\")\n        }\n        guard status2.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container2 hostname failed with status \\(status2)\")\n        }\n\n        // Container1 should have the pod-level hostname\n        guard String(data: buffer1.data, encoding: .utf8) == \"pod-host\\n\" else {\n            throw IntegrationError.assert(msg: \"container1 should have pod-level hostname 'pod-host', got: '\\(String(data: buffer1.data, encoding: .utf8) ?? \"nil\")'\")\n        }\n        // Container2 should have its own hostname, not the pod-level one\n        guard String(data: buffer2.data, encoding: .utf8) == \"container-host\\n\" else {\n            throw IntegrationError.assert(msg: \"container2 should have container-level hostname 'container-host', got: '\\(String(data: buffer2.data, encoding: .utf8) ?? \"nil\")'\")\n        }\n    }\n\n    func testPodRLimitOpenFiles() async throws {\n        let id = \"test-pod-rlimit-open-files\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        let buffer = BufferWriter()\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"sh\", \"-c\", \"ulimit -n\"]\n            config.process.rlimits = [\n                LinuxRLimit(kind: .openFiles, hard: 2048, soft: 1024)\n            ]\n            config.process.stdout = buffer\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n\n        let status = try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard let output = String(data: buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {\n            throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n        }\n\n        // ulimit -n returns the soft limit\n        guard output == \"1024\" else {\n            throw IntegrationError.assert(msg: \"expected soft limit '1024', got '\\(output)'\")\n        }\n    }\n\n    func testPodRLimitExec() async throws {\n        let id = \"test-pod-rlimit-exec\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n        }\n\n        do {\n            try await pod.create()\n            try await pod.startContainer(\"container1\")\n\n            // Exec a process with rlimits set\n            let buffer = BufferWriter()\n            let exec = try await pod.execInContainer(\"container1\", processID: \"rlimit-exec\") { config in\n                config.arguments = [\"sh\", \"-c\", \"ulimit -n\"]\n                config.rlimits = [\n                    LinuxRLimit(kind: .openFiles, hard: 512, soft: 256)\n                ]\n                config.stdout = buffer\n            }\n\n            try await exec.start()\n            let status = try await exec.wait()\n            try await exec.delete()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"exec status \\(status) != 0\")\n            }\n\n            guard let output = String(data: buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {\n                throw IntegrationError.assert(msg: \"failed to convert stdout to UTF8\")\n            }\n\n            guard output == \"256\" else {\n                throw IntegrationError.assert(msg: \"expected soft limit '256', got '\\(output)'\")\n            }\n\n            try await pod.killContainer(\"container1\", signal: SIGKILL)\n            try await pod.waitContainer(\"container1\")\n            try await pod.stop()\n        } catch {\n            try? await pod.stop()\n            throw error\n        }\n    }\n\n    func testPodUseInitBasic() async throws {\n        let id = \"test-pod-use-init-basic\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        let buffer = BufferWriter()\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"/bin/echo\", \"hello from pod init\"]\n            config.process.stdout = buffer\n            config.useInit = true\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n\n        let status = try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n        }\n\n        guard String(data: buffer.data, encoding: .utf8) == \"hello from pod init\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"expected 'hello from pod init', got '\\(String(data: buffer.data, encoding: .utf8) ?? \"nil\")'\")\n        }\n    }\n\n    func testPodUseInitExitCodePropagation() async throws {\n        let id = \"test-pod-use-init-exit-code\"\n\n        let bs = try await bootstrap(id)\n\n        // Test exit code 0\n        var pod = try LinuxPod(\"\\(id)-success\", vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"success\")) { config in\n            config.process.arguments = [\"/bin/true\"]\n            config.useInit = true\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n        var status = try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"expected exit code 0, got \\(status.exitCode)\")\n        }\n\n        // Test non-zero exit code\n        pod = try LinuxPod(\"\\(id)-failure\", vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"failure\")) { config in\n            config.process.arguments = [\"/bin/false\"]\n            config.useInit = true\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n        status = try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        guard status.exitCode == 1 else {\n            throw IntegrationError.assert(msg: \"expected exit code 1, got \\(status.exitCode)\")\n        }\n\n        // Test custom exit code\n        pod = try LinuxPod(\"\\(id)-custom\", vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"custom\")) { config in\n            config.process.arguments = [\"sh\", \"-c\", \"exit 42\"]\n            config.useInit = true\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n        status = try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        guard status.exitCode == 42 else {\n            throw IntegrationError.assert(msg: \"expected exit code 42, got \\(status.exitCode)\")\n        }\n    }\n\n    func testPodUseInitSignalForwarding() async throws {\n        let id = \"test-pod-use-init-signal\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"sleep\", \"300\"]\n            config.useInit = true\n        }\n\n        do {\n            try await pod.create()\n            try await pod.startContainer(\"container1\")\n\n            try await Task.sleep(for: .milliseconds(100))\n\n            // Send SIGTERM, should be forwarded to the child and cause exit\n            try await pod.killContainer(\"container1\", signal: SIGTERM)\n\n            let status = try await pod.waitContainer(\"container1\", timeoutInSeconds: 5)\n            try await pod.stop()\n\n            // SIGTERM should result in exit code 128 + 15 = 143\n            guard status.exitCode == 143 else {\n                throw IntegrationError.assert(msg: \"expected exit code 143 (SIGTERM), got \\(status.exitCode)\")\n            }\n        } catch {\n            try? await pod.stop()\n            throw error\n        }\n    }\n\n    func testPodUseInitMultipleContainers() async throws {\n        let id = \"test-pod-use-init-multiple\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        let buffer1 = BufferWriter()\n        let buffer2 = BufferWriter()\n\n        // Container1 with useInit\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"/bin/echo\", \"container1 with init\"]\n            config.process.stdout = buffer1\n            config.useInit = true\n        }\n\n        // Container2 without useInit\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.process.arguments = [\"/bin/echo\", \"container2 without init\"]\n            config.process.stdout = buffer2\n            config.useInit = false\n        }\n\n        try await pod.create()\n\n        try await pod.startContainer(\"container1\")\n        let status1 = try await pod.waitContainer(\"container1\")\n\n        try await pod.startContainer(\"container2\")\n        let status2 = try await pod.waitContainer(\"container2\")\n\n        try await pod.stop()\n\n        guard status1.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container1 exit code \\(status1.exitCode) != 0\")\n        }\n\n        guard status2.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container2 exit code \\(status2.exitCode) != 0\")\n        }\n\n        guard String(data: buffer1.data, encoding: .utf8) == \"container1 with init\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"container1 output mismatch: '\\(String(data: buffer1.data, encoding: .utf8) ?? \"nil\")'\")\n        }\n\n        guard String(data: buffer2.data, encoding: .utf8) == \"container2 without init\\n\" else {\n            throw IntegrationError.assert(\n                msg: \"container2 output mismatch: '\\(String(data: buffer2.data, encoding: .utf8) ?? \"nil\")'\")\n        }\n    }\n\n    func testPodUseInitWithSharedPIDNamespace() async throws {\n        let id = \"test-pod-use-init-shared-pid\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n            config.shareProcessNamespace = true\n        }\n\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.process.arguments = [\"sleep\", \"300\"]\n            config.useInit = true\n        }\n\n        let psBuffer = BufferWriter()\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            // Check if we can see container1's sleep process through the shared PID namespace\n            config.process.arguments = [\"sh\", \"-c\", \"ps aux | grep 'sleep 300' | grep -v grep\"]\n            config.process.stdout = psBuffer\n        }\n\n        try await pod.create()\n        try await pod.startContainer(\"container1\")\n        try await Task.sleep(for: .milliseconds(100))\n\n        try await pod.startContainer(\"container2\")\n        let status = try await pod.waitContainer(\"container2\")\n\n        try await pod.killContainer(\"container1\", signal: SIGKILL)\n        _ = try await pod.waitContainer(\"container1\")\n        try await pod.stop()\n\n        guard status.exitCode == 0 else {\n            throw IntegrationError.assert(msg: \"container2 should have found the sleep process (status: \\(status))\")\n        }\n\n        let output = String(data: psBuffer.data, encoding: .utf8) ?? \"\"\n        guard output.contains(\"sleep 300\") else {\n            throw IntegrationError.assert(msg: \"ps output should contain 'sleep 300', got: '\\(output)'\")\n        }\n    }\n\n    func testPodUnixSocketIntoGuestSymlink() async throws {\n        let id = \"test-pod-unixsocket-into-guest-symlink\"\n\n        let bs = try await bootstrap(id)\n\n        let hostSocketPath = try createPodHostUnixSocket()\n\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        // Use /var/run/test.sock. Alpine has /var/run -> /run symlink\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.process.arguments = [\"sleep\", \"100\"]\n            config.sockets = [\n                UnixSocketConfiguration(\n                    source: URL(filePath: hostSocketPath),\n                    destination: URL(filePath: \"/var/run/test.sock\"),\n                    direction: .into\n                )\n            ]\n        }\n\n        do {\n            try await pod.create()\n            try await pod.startContainer(\"container1\")\n\n            let buffer = BufferWriter()\n            let lsExec = try await pod.execInContainer(\"container1\", processID: \"ls-socket\") { config in\n                config.arguments = [\"ls\", \"-l\", \"/var/run/test.sock\"]\n                config.stdout = buffer\n            }\n\n            try await lsExec.start()\n            let status2 = try await lsExec.wait()\n            try await lsExec.delete()\n\n            guard status2.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"ls command failed with status \\(status2)\")\n            }\n\n            guard let lsOutput = String(data: buffer.data, encoding: .utf8) else {\n                throw IntegrationError.assert(msg: \"failed to convert ls output to UTF8\")\n            }\n\n            guard lsOutput.hasPrefix(\"s\") else {\n                throw IntegrationError.assert(\n                    msg: \"expected socket file (starting with 's'), got: \\(lsOutput)\")\n            }\n\n            try await pod.killContainer(\"container1\", signal: SIGKILL)\n            _ = try await pod.waitContainer(\"container1\")\n            try await pod.stop()\n        } catch {\n            try? await pod.stop()\n            throw error\n        }\n    }\n\n    private func createPodHostUnixSocket() throws -> String {\n        let dir = FileManager.default.uniqueTemporaryDirectory(create: true)\n        let socketPath = dir.appendingPathComponent(\"test.sock\").path\n\n        let socket = try Socket(type: UnixType(path: socketPath))\n        try socket.listen()\n\n        return socketPath\n    }\n\n    func testPodSysctl() async throws {\n        let id = \"test-pod-sysctl\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        let buffer = BufferWriter()\n        try await pod.addContainer(\"container1\", rootfs: bs.rootfs) { config in\n            config.sysctl = [\n                \"net.core.somaxconn\": \"4096\"\n            ]\n            config.process.arguments = [\"cat\", \"/proc/sys/net/core/somaxconn\"]\n            config.process.stdout = buffer\n        }\n\n        do {\n            try await pod.create()\n            try await pod.startContainer(\"container1\")\n\n            let status = try await pod.waitContainer(\"container1\")\n            try await pod.stop()\n\n            guard status.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"process status \\(status) != 0\")\n            }\n\n            let output = String(data: buffer.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)\n            guard output == \"4096\" else {\n                throw IntegrationError.assert(\n                    msg: \"sysctl net.core.somaxconn should be '4096', got '\\(output ?? \"nil\")'\")\n            }\n        } catch {\n            try? await pod.stop()\n            throw error\n        }\n    }\n\n    func testPodSysctlMultipleContainers() async throws {\n        let id = \"test-pod-sysctl-multi\"\n\n        let bs = try await bootstrap(id)\n        let pod = try LinuxPod(id, vmm: bs.vmm) { config in\n            config.cpus = 4\n            config.memoryInBytes = 1024.mib()\n            config.bootLog = bs.bootLog\n        }\n\n        // Containers in a pod share a network namespace, so use different\n        // sysctls per container to avoid clobbering.\n        let buffer1 = BufferWriter()\n        try await pod.addContainer(\"container1\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container1\")) { config in\n            config.sysctl = [\n                \"net.core.somaxconn\": \"2048\"\n            ]\n            config.process.arguments = [\"cat\", \"/proc/sys/net/core/somaxconn\"]\n            config.process.stdout = buffer1\n        }\n\n        let buffer2 = BufferWriter()\n        try await pod.addContainer(\"container2\", rootfs: try cloneRootfs(bs.rootfs, testID: id, containerID: \"container2\")) { config in\n            config.sysctl = [\n                \"net.core.netdev_max_backlog\": \"5000\"\n            ]\n            config.process.arguments = [\"cat\", \"/proc/sys/net/core/netdev_max_backlog\"]\n            config.process.stdout = buffer2\n        }\n\n        do {\n            try await pod.create()\n\n            try await pod.startContainer(\"container1\")\n            let status1 = try await pod.waitContainer(\"container1\")\n            guard status1.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"container1 status \\(status1) != 0\")\n            }\n            let output1 = String(data: buffer1.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)\n            guard output1 == \"2048\" else {\n                throw IntegrationError.assert(\n                    msg: \"container1 sysctl net.core.somaxconn should be '2048', got '\\(output1 ?? \"nil\")'\")\n            }\n\n            try await pod.startContainer(\"container2\")\n            let status2 = try await pod.waitContainer(\"container2\")\n            guard status2.exitCode == 0 else {\n                throw IntegrationError.assert(msg: \"container2 status \\(status2) != 0\")\n            }\n            let output2 = String(data: buffer2.data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines)\n            guard output2 == \"5000\" else {\n                throw IntegrationError.assert(\n                    msg: \"container2 sysctl net.core.netdev_max_backlog should be '5000', got '\\(output2 ?? \"nil\")'\")\n            }\n\n            try await pod.stop()\n        } catch {\n            try? await pod.stop()\n            throw error\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/Integration/Suite.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ArgumentParser\nimport Containerization\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport ContainerizationOS\nimport Foundation\nimport Logging\nimport NIOCore\nimport NIOPosix\nimport Synchronization\n\nactor UnpackCoordinator {\n    private var inFlight: [String: Task<Containerization.Mount, Error>] = [:]\n\n    func unpack(\n        key: String,\n        operation: @escaping @Sendable () async throws -> Containerization.Mount\n    ) async throws -> Containerization.Mount {\n        if let existing = inFlight[key] {\n            return try await existing.value\n        }\n\n        let task = Task {\n            try await operation()\n        }\n        inFlight[key] = task\n\n        defer {\n            inFlight.removeValue(forKey: key)\n        }\n\n        return try await task.value\n    }\n}\n\nstruct Test: Sendable {\n    var name: String\n    var work: @Sendable () async throws -> Void\n\n    init(_ name: String, _ work: @escaping @Sendable () async throws -> Void) {\n        self.name = name\n        self.work = work\n    }\n}\n\nfinal class JobQueue<T>: Sendable where T: Sendable {\n    struct State: Sendable {\n        var next = 0\n        var jobs: [T]\n    }\n\n    private let lock: Mutex<State>\n    init(_ jobs: [T]) {\n        self.lock = Mutex(State(jobs: jobs))\n    }\n\n    func pop() -> T? {\n        self.lock.withLock { state in\n            guard state.next < state.jobs.count else {\n                return nil\n            }\n            defer {\n                state.next += 1\n            }\n            return state.jobs[state.next]\n        }\n    }\n}\n\nlet log = {\n    LoggingSystem.bootstrap(StreamLogHandler.standardError)\n    var log = Logger(label: \"com.apple.containerization\")\n    log.logLevel = .debug\n    return log\n}()\n\nenum IntegrationError: Swift.Error {\n    case assert(msg: String)\n    case noOutput\n}\n\nstruct SkipTest: Swift.Error, CustomStringConvertible {\n    let reason: String\n\n    var description: String {\n        reason\n    }\n}\n\n@main\nstruct IntegrationSuite: AsyncParsableCommand {\n    static let appRoot: URL = {\n        FileManager.default.urls(\n            for: .applicationSupportDirectory,\n            in: .userDomainMask\n        ).first!\n        .appendingPathComponent(\"com.apple.containerization\")\n    }()\n\n    private static let _contentStore: ContentStore = {\n        try! LocalContentStore(path: appRoot.appending(path: \"content\"))\n    }()\n\n    private static let _imageStore: ImageStore = {\n        try! ImageStore(\n            path: appRoot,\n            contentStore: contentStore\n        )\n    }()\n\n    static let _testDir: URL = {\n        FileManager.default.uniqueTemporaryDirectory(create: true)\n    }()\n\n    static var testDir: URL {\n        _testDir\n    }\n\n    static var imageStore: ImageStore {\n        _imageStore\n    }\n\n    static var contentStore: ContentStore {\n        _contentStore\n    }\n\n    static let initImage = \"vminit:latest\"\n\n    private static let unpackCoordinator = UnpackCoordinator()\n\n    @Option(name: .shortAndLong, help: \"Path to a directory for boot logs\")\n    var bootlogDir: String = \"./bin/integration-bootlogs\"\n\n    @Option(name: .shortAndLong, help: \"Path to a kernel binary\")\n    var kernel: String = \"./bin/vmlinux\"\n\n    @Option(name: .shortAndLong, help: \"Maximum number of concurrent tests\")\n    var maxConcurrency: Int = 4\n\n    @Option(name: .shortAndLong, help: \"Only run tests whose names contain this string\")\n    var filter: String?\n\n    static func binPath(name: String) -> URL {\n        URL(fileURLWithPath: FileManager.default.currentDirectoryPath)\n            .appendingPathComponent(\"bin\")\n            .appendingPathComponent(name)\n    }\n\n    static let eventLoop = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)\n\n    func bootstrap(_ testID: String) async throws -> (rootfs: Containerization.Mount, vmm: VirtualMachineManager, image: Containerization.Image, bootLog: BootLog) {\n        let reference = \"ghcr.io/linuxcontainers/alpine:3.20\"\n        let store = Self.imageStore\n\n        let initImage = try await store.getInitImage(reference: Self.initImage)\n        let initfs = try await {\n            let p = Self.binPath(name: \"init.block\")\n            do {\n                return try await initImage.initBlock(at: p, for: .linuxArm)\n            } catch let err as ContainerizationError {\n                guard err.code == .exists else {\n                    throw err\n                }\n                return .block(\n                    format: \"ext4\",\n                    source: p.absolutePath(),\n                    destination: \"/\",\n                    options: [\"ro\"]\n                )\n            }\n        }()\n\n        var testKernel = Kernel(path: .init(filePath: kernel), platform: .linuxArm)\n        testKernel.commandLine.addDebug()\n        let image = try await Self.fetchImage(reference: reference, store: store)\n        let platform = Platform(arch: \"arm64\", os: \"linux\", variant: \"v8\")\n\n        // Unpack to shared location with coordination to prevent concurrent unpacks\n        let fsPath = Self.testDir.appending(component: image.digest)\n        let fs = try await Self.unpackCoordinator.unpack(key: fsPath.absolutePath()) {\n            do {\n                let unpacker = EXT4Unpacker(blockSizeInBytes: 2.gib())\n                return try await unpacker.unpack(image, for: platform, at: fsPath)\n            } catch let err as ContainerizationError {\n                if err.code == .exists {\n                    return .block(\n                        format: \"ext4\",\n                        source: fsPath.absolutePath(),\n                        destination: \"/\",\n                        options: []\n                    )\n                }\n                throw err\n            }\n        }\n\n        // Clone to test-specific path\n        let clPath = Self.testDir.appending(component: \"\\(testID).ext4\").absolutePath()\n        try? FileManager.default.removeItem(atPath: clPath)\n\n        let cl = try fs.clone(to: clPath)\n\n        // Create bootLog directory and per-container bootLog path\n        let bootlogDirURL = URL(filePath: bootlogDir)\n        try? FileManager.default.createDirectory(at: bootlogDirURL, withIntermediateDirectories: true)\n        let bootlogURL = bootlogDirURL.appendingPathComponent(\"\\(testID).log\")\n\n        return (\n            cl,\n            VZVirtualMachineManager(\n                kernel: testKernel,\n                initialFilesystem: initfs,\n                group: Self.eventLoop\n            ),\n            image,\n            BootLog.file(path: bootlogURL)\n        )\n    }\n\n    static func fetchImage(reference: String, store: ImageStore) async throws -> Containerization.Image {\n        do {\n            return try await store.get(reference: reference)\n        } catch let error as ContainerizationError {\n            if error.code == .notFound {\n                return try await store.pull(reference: reference)\n            }\n            throw error\n        }\n    }\n\n    static func adjustLimits() throws {\n        var limits = rlimit()\n        guard getrlimit(RLIMIT_NOFILE, &limits) == 0 else {\n            throw POSIXError(.init(rawValue: errno)!)\n        }\n        limits.rlim_cur = 65536\n        limits.rlim_max = 65536\n\n        guard setrlimit(RLIMIT_NOFILE, &limits) == 0 else {\n            throw POSIXError(.init(rawValue: errno)!)\n        }\n    }\n\n    private func macOS26Tests() -> [Test] {\n        if #available(macOS 26.0, *) {\n            return [\n                Test(\"container interface custom MTU\", testInterfaceMTU),\n                Test(\"container networking disabled\", testNetworkingDisabled),\n                Test(\"container networking enabled\", testNetworkingEnabled),\n            ]\n        }\n        return []\n    }\n\n    // Why does this exist?\n    //\n    // We need the virtualization entitlement to execute these tests.\n    // There currently does not exist a straightforward way to do this\n    // in a pure swift package.\n    //\n    // In order to not have a dependency on xcode, we create an executable\n    // for our integration tests that can be signed then ran.\n    //\n    // We also can't import Testing as it expects to be run from a runner.\n    // Hopefully this improves over time.\n    func run() async throws {\n        try Self.adjustLimits()\n        let suiteStarted = CFAbsoluteTimeGetCurrent()\n        log.info(\"starting integration suite\\n\")\n\n        let tests: [Test] =\n            [\n                // Containers\n                Test(\"process true\", testProcessTrue),\n                Test(\"process false\", testProcessFalse),\n                Test(\"process echo hi\", testProcessEchoHi),\n                Test(\"process no executable\", testProcessNoExecutable),\n                Test(\"process user\", testProcessUser),\n                Test(\"process stdin\", testProcessStdin),\n                Test(\"process home envvar\", testProcessHomeEnvvar),\n                Test(\"process custom home envvar\", testProcessCustomHomeEnvvar),\n                Test(\"process tty ensure TERM\", testProcessTtyEnvvar),\n                Test(\"multiple concurrent processes\", testMultipleConcurrentProcesses),\n                Test(\"multiple concurrent processes with output stress\", testMultipleConcurrentProcessesOutputStress),\n                Test(\"container hostname\", testHostname),\n                Test(\"container hostname defaults to container id\", testHostnameDefaultsToContainerID),\n                Test(\"container hosts\", testHostsFile),\n                Test(\"container mount\", testMounts),\n                Test(\"container stop idempotency\", testContainerStopIdempotency),\n                Test(\"nested virt\", testNestedVirtualizationEnabled),\n                Test(\"container manager\", testContainerManagerCreate),\n                Test(\"container reuse\", testContainerReuse),\n                Test(\"container /dev/console\", testContainerDevConsole),\n                Test(\"container statistics\", testContainerStatistics),\n                Test(\"container cgroup limits\", testCgroupLimits),\n                Test(\"container memory events OOM kill\", testMemoryEventsOOMKill),\n                Test(\"container no serial console\", testNoSerialConsole),\n                Test(\"unix socket into guest\", testUnixSocketIntoGuest),\n                Test(\"unix socket into guest symlink\", testUnixSocketIntoGuestSymlink),\n                Test(\"container non-closure constructor\", testNonClosureConstructor),\n                Test(\"container test large stdio ingest\", testLargeStdioOutput),\n                Test(\"process delete idempotency\", testProcessDeleteIdempotency),\n                Test(\"multiple execs without delete\", testMultipleExecsWithoutDelete),\n                Test(\"container bootlog using filehandle\", testBootLogFileHandle),\n                Test(\"container capabilities sys admin\", testCapabilitiesSysAdmin),\n                Test(\"container capabilities net admin\", testCapabilitiesNetAdmin),\n                Test(\"container capabilities OCI default\", testCapabilitiesOCIDefault),\n                Test(\"container capabilities all capabilities\", testCapabilitiesAllCapabilities),\n                Test(\"container capabilities file ownership\", testCapabilitiesFileOwnership),\n                Test(\"container copy in\", testCopyIn),\n                Test(\"container copy out\", testCopyOut),\n                Test(\"container copy large file\", testCopyLargeFile),\n                Test(\"container copy in directory\", testCopyInDirectory),\n                Test(\"container copy out directory\", testCopyOutDirectory),\n                Test(\"container copy empty file\", testCopyEmptyFile),\n                Test(\"container copy empty directory\", testCopyEmptyDirectory),\n                Test(\"container copy binary file\", testCopyBinaryFile),\n                Test(\"container copy multiple files\", testCopyMultipleFiles),\n                Test(\"container copy directory round trip\", testCopyDirectoryRoundTrip),\n                Test(\"container copy in create parents\", testCopyInCreateParents),\n                Test(\"container copy file permissions\", testCopyFilePermissions),\n                Test(\"container copy large directory\", testCopyLargeDirectory),\n                Test(\"container read-only rootfs\", testReadOnlyRootfs),\n                Test(\"container read-only rootfs hosts file\", testReadOnlyRootfsHostsFileWritten),\n                Test(\"container read-only rootfs DNS\", testReadOnlyRootfsDNSConfigured),\n                Test(\"container writable layer\", testWritableLayer),\n                Test(\"container writable layer preserves lower\", testWritableLayerPreservesLowerLayer),\n                Test(\"container writable layer reads from lower\", testWritableLayerReadsFromLower),\n                Test(\"container writable layer with ro lower\", testWritableLayerWithReadOnlyLower),\n                Test(\"container writable layer size\", testWritableLayerSize),\n                Test(\"container writable layer DNS and hosts\", testWritableLayerWithDNSAndHosts),\n                Test(\"large stdin input\", testLargeStdinInput),\n                Test(\"exec large stdin input\", testExecLargeStdinInput),\n                Test(\"exec custom path resolution\", testExecCustomPathResolution),\n                Test(\"stdin explicit close\", testStdinExplicitClose),\n                Test(\"stdin binary data\", testStdinBinaryData),\n                Test(\"stdin multiple chunks\", testStdinMultipleChunks),\n                Test(\"stdin very large\", testStdinVeryLarge),\n                // FIXME: reenable when single file mount issues resolved\n                //Test(\"container single file mount\", testSingleFileMount),\n                //Test(\"container single file mount read-only\", testSingleFileMountReadOnly),\n                //Test(\"container single file mount write-back\", testSingleFileMountWriteBack),\n                //Test(\"container single file mount symlink\", testSingleFileMountSymlink),\n                Test(\"container rlimit open files\", testRLimitOpenFiles),\n                Test(\"container rlimit multiple\", testRLimitMultiple),\n                Test(\"container rlimit exec\", testRLimitExec),\n                Test(\"container duplicate virtiofs mount\", testDuplicateVirtiofsMount),\n                Test(\"container duplicate virtiofs mount via symlink\", testDuplicateVirtiofsMountViaSymlink),\n                Test(\"container useInit basic\", testUseInitBasic),\n                Test(\"container useInit exit code propagation\", testUseInitExitCodePropagation),\n                Test(\"container useInit signal forwarding\", testUseInitSignalForwarding),\n                Test(\"container useInit zombie reaping\", testUseInitZombieReaping),\n                Test(\"container useInit with terminal\", testUseInitWithTerminal),\n                Test(\"container useInit with stdin\", testUseInitWithStdin),\n                Test(\"container sysctl\", testSysctl),\n                Test(\"container sysctl multiple\", testSysctlMultiple),\n                Test(\"container noNewPrivileges\", testNoNewPrivileges),\n                Test(\"container noNewPrivileges disabled\", testNoNewPrivilegesDisabled),\n                Test(\"container noNewPrivileges exec\", testNoNewPrivilegesExec),\n                Test(\"container workingDir created\", testWorkingDirCreated),\n                Test(\"container workingDir exec created\", testWorkingDirExecCreated),\n\n                // Pods\n                Test(\"pod single container\", testPodSingleContainer),\n                Test(\"pod multiple containers\", testPodMultipleContainers),\n                Test(\"pod container output\", testPodContainerOutput),\n                Test(\"pod concurrent containers\", testPodConcurrentContainers),\n                Test(\"pod exec in container\", testPodExecInContainer),\n                Test(\"pod exec in container env\", testPodExecInContainerEnv),\n                Test(\"pod container hostname\", testPodContainerHostname),\n                Test(\"pod container hostname defaults to container id\", testPodContainerHostnameDefaultsToContainerID),\n                Test(\"pod stop container idempotency\", testPodStopContainerIdempotency),\n                Test(\"pod list containers\", testPodListContainers),\n                Test(\"pod container statistics\", testPodContainerStatistics),\n                Test(\"pod memory events OOM kill\", testPodMemoryEventsOOMKill),\n                Test(\"pod container resource limits\", testPodContainerResourceLimits),\n                Test(\"pod container filesystem isolation\", testPodContainerFilesystemIsolation),\n                Test(\"pod container PID namespace isolation\", testPodContainerPIDNamespaceIsolation),\n                Test(\"pod container independent resource limits\", testPodContainerIndependentResourceLimits),\n                Test(\"pod shared PID namespace\", testPodSharedPIDNamespace),\n                Test(\"pod read-only rootfs\", testPodReadOnlyRootfs),\n                Test(\"pod read-only rootfs DNS\", testPodReadOnlyRootfsDNSConfigured),\n                //Test(\"pod single file mount\", testPodSingleFileMount),\n                Test(\"pod container hosts config\", testPodContainerHostsConfig),\n                Test(\"pod multiple containers different DNS\", testPodMultipleContainersDifferentDNS),\n                Test(\"pod multiple containers different hosts\", testPodMultipleContainersDifferentHosts),\n                Test(\"pod level DNS\", testPodLevelDNS),\n                Test(\"pod level DNS with container override\", testPodLevelDNSWithContainerOverride),\n                Test(\"pod level hosts\", testPodLevelHosts),\n                Test(\"pod level hosts with container override\", testPodLevelHostsWithContainerOverride),\n                Test(\"pod level hostname\", testPodLevelHostname),\n                Test(\"pod level hostname with container override\", testPodLevelHostnameWithContainerOverride),\n                Test(\"pod rlimit open files\", testPodRLimitOpenFiles),\n                Test(\"pod rlimit exec\", testPodRLimitExec),\n                Test(\"pod useInit basic\", testPodUseInitBasic),\n                Test(\"pod useInit exit code propagation\", testPodUseInitExitCodePropagation),\n                Test(\"pod useInit signal forwarding\", testPodUseInitSignalForwarding),\n                Test(\"pod useInit multiple containers\", testPodUseInitMultipleContainers),\n                Test(\"pod useInit with shared PID namespace\", testPodUseInitWithSharedPIDNamespace),\n                Test(\"pod unix socket into guest symlink\", testPodUnixSocketIntoGuestSymlink),\n                Test(\"pod sysctl\", testPodSysctl),\n                Test(\"pod sysctl multiple containers\", testPodSysctlMultipleContainers),\n            ] + macOS26Tests()\n\n        let filteredTests: [Test]\n        if let filter {\n            filteredTests = tests.filter { $0.name.contains(filter) }\n            log.info(\"filter '\\(filter)' matched \\(filteredTests.count)/\\(tests.count) tests\")\n        } else {\n            filteredTests = tests\n        }\n\n        let passed: Atomic<Int> = Atomic(0)\n        let skipped: Atomic<Int> = Atomic(0)\n\n        await withTaskGroup(of: Void.self) { group in\n            let jobQueue = JobQueue(filteredTests)\n            for _ in 0..<maxConcurrency {\n                group.addTask { @Sendable in\n                    while let job = jobQueue.pop() {\n                        do {\n                            log.info(\"test \\(job.name) started...\")\n\n                            let started = CFAbsoluteTimeGetCurrent()\n                            try await job.work()\n                            let lasted = CFAbsoluteTimeGetCurrent() - started\n\n                            log.info(\"✅ test \\(job.name) complete in \\(lasted)s.\")\n                            passed.add(1, ordering: .relaxed)\n                        } catch let err as SkipTest {\n                            log.info(\"⏭️ skipped test: \\(err)\")\n                            skipped.add(1, ordering: .relaxed)\n                        } catch {\n                            log.error(\"❌ test \\(job.name) failed: \\(error)\")\n                        }\n                    }\n                }\n            }\n            await group.waitForAll()\n        }\n\n        let passedCount = passed.load(ordering: .acquiring)\n        let skippedCount = skipped.load(ordering: .acquiring)\n\n        let ended = CFAbsoluteTimeGetCurrent() - suiteStarted\n        var finishingText = \"\\n\\nIntegration suite completed in \\(ended)s with \\(passedCount)/\\(filteredTests.count) passed\"\n        if skipped.load(ordering: .acquiring) > 0 {\n            finishingText += \" and \\(skippedCount)/\\(filteredTests.count) skipped\"\n        }\n        finishingText += \"!\"\n\n        log.info(\"\\(finishingText)\")\n\n        try? FileManager.default.removeItem(at: Self.testDir)\n        if passedCount + skippedCount < filteredTests.count {\n            log.error(\"❌\")\n            throw ExitCode(1)\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/cctl/ImageCommand.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ArgumentParser\nimport Containerization\nimport ContainerizationArchive\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport Foundation\n\nextension Application {\n    struct Images: AsyncParsableCommand {\n        static let configuration = CommandConfiguration(\n            commandName: \"images\",\n            abstract: \"Manage images\",\n            subcommands: [\n                Get.self,\n                Delete.self,\n                Pull.self,\n                Tag.self,\n                Push.self,\n                Save.self,\n                Load.self,\n            ]\n        )\n\n        func run() async throws {\n            let store = Application.imageStore\n            let images = try await store.list()\n\n            print(\"REFERENCE\\tMEDIA TYPE\\tDIGEST\")\n            for image in images {\n                print(\"\\(image.reference)\\t\\(image.mediaType)\\t\\(image.digest)\")\n            }\n        }\n\n        struct Delete: AsyncParsableCommand {\n            @Argument var reference: String\n\n            func run() async throws {\n                let store = Application.imageStore\n                try await store.delete(reference: reference)\n            }\n        }\n\n        struct Tag: AsyncParsableCommand {\n            @Argument var old: String\n            @Argument var new: String\n\n            func run() async throws {\n                let store = Application.imageStore\n                _ = try await store.tag(existing: old, new: new)\n            }\n        }\n\n        struct Get: AsyncParsableCommand {\n            @Argument var reference: String\n\n            func run() async throws {\n                let store = Application.imageStore\n                let image = try await store.get(reference: reference)\n\n                let index = try await image.index()\n\n                let enc = JSONEncoder()\n                enc.outputFormatting = .prettyPrinted\n                let data = try enc.encode(ImageDisplay(reference: image.reference, index: index))\n                print(String(data: data, encoding: .utf8)!)\n            }\n        }\n\n        struct ImageDisplay: Codable {\n            let reference: String\n            let index: Index\n        }\n\n        struct Pull: AsyncParsableCommand {\n            static let configuration = CommandConfiguration(\n                commandName: \"pull\",\n                abstract: \"Pull an image's contents into a content store\"\n            )\n\n            @Argument var ref: String\n\n            @Option(name: .customLong(\"platform\"), help: \"Platform string in the form 'os/arch/variant'. Example 'linux/arm64/v8', 'linux/amd64'\") var platformString: String?\n\n            @Option(\n                name: .customLong(\"unpack-path\"), help: \"Path to a new directory to unpack the image into\",\n                transform: { str in\n                    URL(fileURLWithPath: str, relativeTo: .currentDirectory()).absoluteURL.path(percentEncoded: false)\n                })\n            var unpackPath: String?\n\n            @Flag(help: \"Pull via plain text http\") var http: Bool = false\n\n            func run() async throws {\n                let imageStore = Application.imageStore\n                let platform: Platform? = try {\n                    if let platformString {\n                        return try Platform(from: platformString)\n                    }\n                    return nil\n                }()\n\n                let reference = try Reference.parse(ref)\n                reference.normalize()\n                let normalizedReference = reference.description\n                if normalizedReference != ref {\n                    print(\"Reference resolved to \\(reference.description)\")\n                }\n\n                var startTime = ContinuousClock.now\n                let image = try await Images.withAuthentication(ref: normalizedReference) { auth in\n                    try await imageStore.pull(reference: normalizedReference, platform: platform, insecure: http, auth: auth)\n                }\n\n                guard let image else {\n                    print(\"image pull failed\")\n                    Application.exit(withError: POSIXError(.EACCES))\n                }\n\n                var duration = ContinuousClock.now - startTime\n                print(\"Image pull took: \\(duration)\\n\")\n\n                guard let unpackPath else {\n                    return\n                }\n                guard !FileManager.default.fileExists(atPath: unpackPath) else {\n                    throw ContainerizationError(.exists, message: \"directory already exists at \\(unpackPath)\")\n                }\n                let unpackUrl = URL(filePath: unpackPath)\n                try FileManager.default.createDirectory(at: unpackUrl, withIntermediateDirectories: true)\n\n                let unpacker = EXT4Unpacker.init(blockSizeInBytes: 2.gib())\n\n                startTime = ContinuousClock.now\n                if let platform {\n                    let name = platform.description.replacingOccurrences(of: \"/\", with: \"-\")\n                    let _ = try await unpacker.unpack(image, for: platform, at: unpackUrl.appending(component: name))\n                } else {\n                    for descriptor in try await image.index().manifests {\n                        if let referenceType = descriptor.annotations?[\"vnd.docker.reference.type\"], referenceType == \"attestation-manifest\" {\n                            continue\n                        }\n                        guard let descPlatform = descriptor.platform else {\n                            continue\n                        }\n                        let name = descPlatform.description.replacingOccurrences(of: \"/\", with: \"-\")\n                        let _ = try await unpacker.unpack(image, for: descPlatform, at: unpackUrl.appending(component: name))\n                        print(\"created snapshot for platform \\(descPlatform.description)\")\n                    }\n                }\n                duration = ContinuousClock.now - startTime\n                print(\"\\nUnpacking took: \\(duration)\")\n            }\n        }\n\n        struct Push: AsyncParsableCommand {\n            static let configuration = CommandConfiguration(\n                commandName: \"push\",\n                abstract: \"Push an image to a remote registry\"\n            )\n\n            @Option(help: \"Platform string in the form 'os/arch/variant'. Example 'linux/arm64/v8', 'linux/amd64'\") var platformString: String?\n\n            @Flag(help: \"Push via plain text http\") var http: Bool = false\n\n            @Argument var ref: String\n\n            func run() async throws {\n                let imageStore = Application.imageStore\n                let platform: Platform? = try {\n                    if let platformString {\n                        return try Platform(from: platformString)\n                    }\n                    return nil\n                }()\n\n                let reference = try Reference.parse(ref)\n                reference.normalize()\n                let normalizedReference = reference.description\n                if normalizedReference != ref {\n                    print(\"Reference resolved to \\(reference.description)\")\n                }\n\n                try await Images.withAuthentication(ref: normalizedReference) { auth in\n                    try await imageStore.push(reference: normalizedReference, platform: platform, insecure: http, auth: auth)\n                }\n                print(\"image pushed\")\n            }\n        }\n\n        struct Save: AsyncParsableCommand {\n            static let configuration = CommandConfiguration(\n                commandName: \"save\",\n                abstract: \"Save one or more images to a tar archive\"\n            )\n\n            @Option(help: \"Platform string in the form 'os/arch/variant'. Example 'linux/arm64/v8', 'linux/amd64'\") var platform: String?\n\n            @Option(name: .shortAndLong, help: \"Path to tar archive\")\n            var output: String\n\n            @Argument var reference: [String]\n\n            func run() async throws {\n                var p: Platform? = nil\n                if let platform {\n                    p = try Platform(from: platform)\n                }\n                let store = Application.imageStore\n                let tempDir = FileManager.default.uniqueTemporaryDirectory()\n                defer {\n                    try? FileManager.default.removeItem(at: tempDir)\n                }\n                try await store.save(references: reference, out: tempDir, platform: p)\n                let writer = try ArchiveWriter(format: .pax, filter: .none, file: URL(filePath: output))\n                try writer.archiveDirectory(tempDir)\n                try writer.finishEncoding()\n                print(\"image exported\")\n            }\n        }\n\n        struct Load: AsyncParsableCommand {\n            static let configuration = CommandConfiguration(\n                commandName: \"load\",\n                abstract: \"Load one or more images from a tar archive\"\n            )\n\n            @Option(name: .shortAndLong, help: \"Path to tar archive\")\n            var input: String\n\n            func run() async throws {\n                let store = Application.imageStore\n                let tarFile = URL(fileURLWithPath: input)\n                let reader = try ArchiveReader(file: tarFile.absoluteURL)\n                let tempDir = FileManager.default.uniqueTemporaryDirectory()\n                defer {\n                    try? FileManager.default.removeItem(at: tempDir)\n                }\n                let rejectedPaths = try reader.extractContents(to: tempDir)\n                let imported = try await store.load(from: tempDir)\n                for image in imported {\n                    print(\"imported \\(image.reference)\")\n                }\n                for rejectedPath in rejectedPaths {\n                    print(\"warning: skipped image archive member \\(rejectedPath)\")\n                }\n            }\n        }\n\n        private static func withAuthentication<T>(\n            ref: String, _ body: @Sendable @escaping (_ auth: Authentication?) async throws -> T?\n        ) async throws -> T? {\n            var authentication: Authentication?\n            let ref = try Reference.parse(ref)\n            guard let host = ref.resolvedDomain else {\n                throw ContainerizationError(.invalidArgument, message: \"no host specified in image reference\")\n            }\n            authentication = Self.authenticationFromEnv(host: host)\n            if let authentication {\n                return try await body(authentication)\n            }\n            let keychain = KeychainHelper(securityDomain: Application.keychainID)\n            authentication = try? keychain.lookup(hostname: host)\n            return try await body(authentication)\n        }\n\n        private static func authenticationFromEnv(host: String) -> Authentication? {\n            let env = ProcessInfo.processInfo.environment\n            guard env[\"REGISTRY_HOST\"] == host else {\n                return nil\n            }\n            guard let user = env[\"REGISTRY_USERNAME\"], let password = env[\"REGISTRY_TOKEN\"] else {\n                return nil\n            }\n            return BasicAuthentication(username: user, password: password)\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/cctl/KernelCommand.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ArgumentParser\nimport Containerization\nimport Foundation\n\nextension Application {\n    struct KernelCommand: AsyncParsableCommand {\n        static let configuration = CommandConfiguration(\n            commandName: \"kernel\",\n            abstract: \"Manage kernel images\",\n            subcommands: [\n                Create.self\n            ]\n        )\n\n        struct Create: AsyncParsableCommand {\n            @Option(name: .shortAndLong, help: \"Name for the kernel image\")\n            var name: String\n\n            @Option(name: .long, help: \"Labels to add to the built image of the form <key1>=<value1>, [<key2>=<value2>,...]\")\n            var labels: [String] = []\n\n            @Argument var kernels: [String]\n\n            func run() async throws {\n                let imageStore = Application.imageStore\n                let contentStore = Application.contentStore\n                let labels = Application.parseKeyValuePairs(from: labels)\n                let binaries = try parseBinaries()\n                _ = try await KernelImage.create(\n                    reference: name,\n                    binaries: binaries,\n                    labels: labels,\n                    imageStore: imageStore,\n                    contentStore: contentStore\n                )\n            }\n\n            func parseBinaries() throws -> [Kernel] {\n                var binaries = [Kernel]()\n                for rawBinary in kernels {\n                    let parts = rawBinary.split(separator: \":\")\n                    guard parts.count == 2 else {\n                        throw \"invalid binary format: \\(rawBinary)\"\n                    }\n                    let platform: SystemPlatform\n                    switch parts[1] {\n                    case \"arm64\":\n                        platform = .linuxArm\n                    case \"amd64\":\n                        platform = .linuxAmd\n                    default:\n                        fatalError(\"unsupported platform \\(parts[1])\")\n                    }\n                    binaries.append(\n                        .init(\n                            path: URL(fileURLWithPath: String(parts[0])),\n                            platform: platform\n                        )\n                    )\n                }\n                return binaries\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/cctl/LoginCommand.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ArgumentParser\nimport Containerization\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport Foundation\n\nextension Application {\n    struct Login: AsyncParsableCommand {\n\n        static let configuration = CommandConfiguration(\n            commandName: \"login\",\n            abstract: \"Login to a registry\"\n        )\n\n        @OptionGroup() var application: Application\n\n        @Option(name: .shortAndLong, help: \"Username\")\n        var username: String = \"\"\n\n        @Flag(help: \"Take the password from stdin\")\n        var passwordStdin: Bool = false\n\n        @Argument(help: \"Registry server name\")\n        var server: String\n\n        @Flag(help: \"Use plain text http to authenticate\") var http: Bool = false\n\n        func run() async throws {\n            var username = self.username\n            var password = \"\"\n            if passwordStdin {\n                if username == \"\" {\n                    throw ContainerizationError(.invalidArgument, message: \"must provide --username with --password-stdin\")\n                }\n                guard let passwordData = try FileHandle.standardInput.readToEnd() else {\n                    throw ContainerizationError(.invalidArgument, message: \"failed to read password from stdin\")\n                }\n                password = String(decoding: passwordData, as: UTF8.self).trimmingCharacters(in: .whitespacesAndNewlines)\n            }\n            let keychain = KeychainHelper(securityDomain: Application.keychainID)\n            if username == \"\" {\n                username = try keychain.userPrompt(hostname: server)\n            }\n            if password == \"\" {\n                password = try keychain.passwordPrompt()\n                print()\n            }\n\n            let server = Reference.resolveDomain(domain: self.server)\n            let scheme = http ? \"http\" : \"https\"\n            let client = RegistryClient(\n                host: server,\n                scheme: scheme,\n                authentication: BasicAuthentication(username: username, password: password),\n                retryOptions: .init(\n                    maxRetries: 10,\n                    retryInterval: 300_000_000,\n                    shouldRetry: ({ response in\n                        response.status.code >= 500\n                    })\n                ),\n                tlsConfiguration: TLSUtils.makeEnvironmentAwareTLSConfiguration(),\n            )\n            try await client.ping()\n            try keychain.save(hostname: server, username: username, password: password)\n            print(\"Login succeeded\")\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/cctl/RootfsCommand.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ArgumentParser\nimport Containerization\nimport ContainerizationArchive\nimport ContainerizationEXT4\nimport ContainerizationError\nimport ContainerizationOCI\nimport ContainerizationOS\nimport Foundation\n\nextension Application {\n    struct Rootfs: AsyncParsableCommand {\n        static let configuration = CommandConfiguration(\n            commandName: \"rootfs\",\n            abstract: \"Manage the root filesystem for a container\",\n            subcommands: [\n                Create.self\n            ]\n        )\n\n        struct Create: AsyncParsableCommand {\n            @Option(name: [.short, .customLong(\"add-file\")], help: \"Additional file to add (format src-path:dst-path)\")\n            var addFiles: [String] = []\n\n            @Option(name: .customLong(\"ext4\"), help: \"The path to an ext4 image to create.\")\n            var ext4File: String?\n\n            @Option(name: .customLong(\"image\"), help: \"The name of the image to produce.\")\n            var imageName: String?\n\n            @Option(name: .customLong(\"label\"), help: \"Label to add to the image (format: key=value)\")\n            var labels: [String] = []\n\n            @Option(name: .long, help: \"Platform of the built binaries being packaged into the block\")\n            var platformString: String = Platform.current.description\n\n            @Option(name: .long, help: \"Path to vmexec\")\n            var vmexec: String\n\n            @Option(name: .long, help: \"Path to vminitd\")\n            var vminitd: String\n\n            @Option(name: .long, help: \"Path to OCI runtime\")\n            var ociRuntime: String?\n\n            // The path where the intermediate tar archive is created.\n            @Argument var tarPath: String\n\n            private static let directories = [\n                \"bin\",\n                \"sbin\",\n                \"dev\",\n                \"sys\",\n                \"proc/self\",  // hack for swift init's booting\n                \"run\",\n                \"tmp\",\n                \"mnt\",\n                \"var\",\n            ]\n\n            func run() async throws {\n                let path = URL(filePath: self.tarPath)\n                try await writeArchive(path: path)\n\n                if let image = self.imageName {\n                    print(\"creating initfs image \\(image)...\")\n                    try await outputImage(\n                        path: path,\n                        reference: image\n                    )\n                }\n\n                if let ext4Path = self.ext4File {\n                    print(\"creating initfs ext4 image at \\(ext4Path)...\")\n                    try await outputExt4(\n                        archive: path,\n                        to: URL(filePath: ext4Path)\n                    )\n                }\n            }\n\n            private func outputExt4(archive: URL, to path: URL) async throws {\n                let unpacker = EXT4Unpacker(blockSizeInBytes: 256.mib())\n                try unpacker.unpack(archive: archive, compression: .gzip, at: path)\n            }\n\n            private func outputImage(path: URL, reference: String) async throws {\n                let p = try Platform(from: platformString)\n                let parsedLabels = Application.parseKeyValuePairs(from: labels)\n                _ = try await InitImage.create(\n                    reference: reference,\n                    rootfs: path,\n                    platform: p,\n                    labels: parsedLabels,\n                    imageStore: Application.imageStore,\n                    contentStore: Application.contentStore\n                )\n            }\n\n            private func writeArchive(path: URL) async throws {\n                let writer = try ArchiveWriter(\n                    format: .pax,\n                    filter: .gzip,\n                    file: path,\n                )\n                let ts = Date()\n                let entry = WriteEntry()\n                entry.permissions = 0o755\n                entry.modificationDate = ts\n                entry.creationDate = ts\n                entry.group = 0\n                entry.owner = 0\n                entry.fileType = .directory\n\n                // create the initial directory structure.\n                for dir in Self.directories {\n                    entry.path = dir\n                    try writer.writeEntry(entry: entry, data: nil)\n                }\n\n                entry.fileType = .regular\n                entry.path = \"sbin/vminitd\"\n\n                var src = URL(fileURLWithPath: vminitd)\n                var data = try Data(contentsOf: src)\n                entry.size = Int64(data.count)\n                try writer.writeEntry(entry: entry, data: data)\n\n                src = URL(fileURLWithPath: vmexec)\n                data = try Data(contentsOf: src)\n                entry.path = \"sbin/vmexec\"\n                entry.size = Int64(data.count)\n                try writer.writeEntry(entry: entry, data: data)\n\n                if let ociRuntimePath = self.ociRuntime {\n                    src = URL(fileURLWithPath: ociRuntimePath)\n                    let fileName = src.lastPathComponent\n                    data = try Data(contentsOf: src)\n                    entry.path = \"sbin/\\(fileName)\"\n                    entry.size = Int64(data.count)\n                    try writer.writeEntry(entry: entry, data: data)\n                }\n\n                for addFile in addFiles {\n                    let paths = addFile.components(separatedBy: \":\")\n                    guard paths.count == 2 else {\n                        throw ContainerizationError(.invalidArgument, message: \"use src-path:dst-path for --add-file\")\n                    }\n                    src = URL(fileURLWithPath: paths[0])\n                    data = try Data(contentsOf: src)\n                    entry.path = paths[1]\n                    entry.size = Int64(data.count)\n                    try writer.writeEntry(entry: entry, data: data)\n                }\n\n                entry.fileType = .symbolicLink\n                entry.path = \"proc/self/exe\"\n                entry.symlinkTarget = \"sbin/vminitd\"\n                entry.size = nil\n                try writer.writeEntry(entry: entry, data: nil)\n                try writer.finishEncoding()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/cctl/RunCommand.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ArgumentParser\nimport Containerization\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport ContainerizationOS\nimport Foundation\n\nextension Application {\n    struct Run: AsyncParsableCommand {\n        static let configuration = CommandConfiguration(\n            commandName: \"run\",\n            abstract: \"Run a container\"\n        )\n\n        @Option(name: [.customLong(\"image\"), .customShort(\"i\")], help: \"Image reference to base the container on\")\n        var imageReference: String = \"docker.io/library/alpine:3.16\"\n\n        @Option(name: .long, help: \"id for the container\")\n        var id: String = \"cctl\"\n\n        @Option(name: [.customLong(\"cpus\"), .customShort(\"c\")], help: \"Number of CPUs to allocate to the container\")\n        var cpus: Int = 2\n\n        @Option(name: [.customLong(\"memory\"), .customShort(\"m\")], help: \"Amount of memory in megabytes\")\n        var memory: UInt64 = 1024\n\n        @Option(name: .customLong(\"fs-size\"), help: \"The size to create the block filesystem as\")\n        var fsSizeInMB: UInt64 = 2048\n\n        @Flag(name: .customLong(\"rosetta\"), help: \"Enable rosetta x64 emulation\")\n        var rosetta = false\n\n        @Option(name: .customLong(\"mount\"), help: \"Directory to share into the container (Example: /foo:/bar)\")\n        var mounts: [String] = []\n\n        @Option(name: .customLong(\"ns\"), help: \"Nameserver addresses\")\n        var nameservers: [String] = []\n\n        @Option(name: .long, help: \"Path to OCI runtime to use for spawning the container\")\n        var ociRuntimePath: String?\n\n        @Flag(name: .long, help: \"Make rootfs readonly\")\n        var readOnly: Bool = false\n\n        @Flag(name: .long, help: \"Run with an init process for signal forwarding and zombie reaping\")\n        var `init`: Bool = false\n\n        @Option(\n            name: [.customLong(\"kernel\"), .customShort(\"k\")], help: \"Kernel binary path\", completion: .file(),\n            transform: { str in\n                URL(fileURLWithPath: str, relativeTo: .currentDirectory()).absoluteURL.path(percentEncoded: false)\n            })\n        public var kernel: String\n\n        @Option(name: .long, help: \"Current working directory\")\n        var cwd: String = \"/\"\n\n        @Argument(parsing: .captureForPassthrough)\n        var arguments: [String] = [\"/bin/sh\"]\n\n        func run() async throws {\n            let kernel = Kernel(\n                path: URL(fileURLWithPath: kernel),\n                platform: .linuxArm\n            )\n\n            // Choose network implementation based on macOS version\n            let network: Network?\n            if #available(macOS 26, *) {\n                network = try VmnetNetwork()\n            } else {\n                network = nil\n            }\n\n            var manager = try await ContainerManager(\n                kernel: kernel,\n                initfsReference: \"vminit:latest\",\n                network: network,\n                rosetta: rosetta\n            )\n            let sigwinchStream = AsyncSignalHandler.create(notify: [SIGWINCH])\n\n            let current = try Terminal.current\n            try current.setraw()\n            defer { current.tryReset() }\n\n            let container = try await manager.create(\n                id,\n                reference: imageReference,\n                rootfsSizeInBytes: fsSizeInMB.mib(),\n                readOnly: readOnly\n            ) { config in\n                config.cpus = cpus\n                config.memoryInBytes = memory.mib()\n                config.process.setTerminalIO(terminal: current)\n                config.process.arguments = arguments\n                config.process.workingDirectory = cwd\n                config.process.capabilities = .allCapabilities\n\n                for mount in self.mounts {\n                    let paths = mount.split(separator: \":\")\n                    if paths.count != 2 {\n                        throw ContainerizationError(\n                            .invalidArgument,\n                            message: \"incorrect mount format detected: \\(mount)\"\n                        )\n                    }\n                    let host = String(paths[0])\n                    let guest = String(paths[1])\n                    let czMount = Containerization.Mount.share(\n                        source: host,\n                        destination: guest\n                    )\n                    config.mounts.append(czMount)\n                }\n\n                var hosts = Hosts.default\n                if !nameservers.isEmpty {\n                    if #available(macOS 26, *) {\n                        config.dns = DNS(nameservers: nameservers)\n                    } else {\n                        print(\"Warning: Networking not supported on macOS < 26, ignoring DNS configuration\")\n                    }\n                }\n\n                // Add host entry for the container using just the IP (not CIDR)\n                if #available(macOS 26, *), !config.interfaces.isEmpty {\n                    let interface = config.interfaces[0]\n                    hosts.entries.append(\n                        Hosts.Entry(\n                            ipAddress: interface.ipv4Address.address.description,\n                            hostnames: [id]\n                        ))\n                }\n\n                config.hosts = hosts\n                if let ociRuntimePath {\n                    config.ociRuntimePath = ociRuntimePath\n                    config.mounts = LinuxContainer.defaultOCIMounts()\n                }\n\n                config.useInit = self.`init`\n            }\n\n            defer {\n                try? manager.delete(id)\n            }\n\n            try await container.create()\n            try await container.start()\n\n            // Resize the containers pty to the current terminal window.\n            try? await container.resize(to: try current.size)\n\n            try await withThrowingTaskGroup(of: Void.self) { group in\n                group.addTask {\n                    for await _ in sigwinchStream.signals {\n                        try await container.resize(to: try current.size)\n                    }\n                }\n\n                try await container.wait()\n                group.cancelAll()\n\n                try await container.stop()\n            }\n        }\n\n        private static let appRoot: URL = {\n            FileManager.default.urls(\n                for: .applicationSupportDirectory,\n                in: .userDomainMask\n            ).first!\n            .appendingPathComponent(\"com.apple.containerization\")\n        }()\n    }\n}\n"
  },
  {
    "path": "Sources/cctl/cctl+Utils.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Containerization\nimport ContainerizationError\nimport ContainerizationOCI\nimport Foundation\n\nextension Application {\n    static func fetchImage(reference: String, store: ImageStore) async throws -> Containerization.Image {\n        do {\n            return try await store.get(reference: reference)\n        } catch let error as ContainerizationError {\n            if error.code == .notFound {\n                return try await store.pull(reference: reference)\n            }\n            throw error\n        }\n    }\n\n    static func parseKeyValuePairs(from items: [String]) -> [String: String] {\n        var parsedLabels: [String: String] = [:]\n        for item in items {\n            let parts = item.split(separator: \"=\", maxSplits: 1)\n            guard parts.count == 2 else {\n                continue\n            }\n            let key = String(parts[0])\n            let val = String(parts[1])\n            parsedLabels[key] = val\n        }\n        return parsedLabels\n    }\n}\n\nextension ContainerizationOCI.Platform {\n    static var arm64: ContainerizationOCI.Platform {\n        .init(arch: \"arm64\", os: \"linux\", variant: \"v8\")\n    }\n}\n"
  },
  {
    "path": "Sources/cctl/cctl.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ArgumentParser\nimport Containerization\nimport ContainerizationOCI\nimport Foundation\nimport Logging\n\nlet log = {\n    LoggingSystem.bootstrap(StreamLogHandler.standardError)\n    var log = Logger(label: \"com.apple.containerization\")\n    log.logLevel = .debug\n    return log\n}()\n\n@main\nstruct Application: AsyncParsableCommand {\n    static let keychainID = \"com.apple.containerization\"\n    static let appRoot: URL = {\n        FileManager.default.urls(\n            for: .applicationSupportDirectory,\n            in: .userDomainMask\n        ).first!\n        .appendingPathComponent(\"com.apple.containerization\")\n    }()\n\n    private static let _contentStore: ContentStore = {\n        try! LocalContentStore(path: appRoot.appendingPathComponent(\"content\"))\n    }()\n\n    private static let _imageStore: ImageStore = {\n        try! ImageStore(\n            path: appRoot,\n            contentStore: contentStore\n        )\n    }()\n\n    static var imageStore: ImageStore {\n        _imageStore\n    }\n\n    static var contentStore: ContentStore {\n        _contentStore\n    }\n\n    static let configuration = CommandConfiguration(\n        commandName: \"cctl\",\n        abstract: \"Utility CLI for Containerization\",\n        version: \"2.0.0\",\n        subcommands: [\n            Images.self,\n            Login.self,\n            Rootfs.self,\n            Run.self,\n        ]\n    )\n}\n\nextension String {\n    var absoluteURL: URL {\n        URL(fileURLWithPath: self).absoluteURL\n    }\n}\n\nextension String: Swift.Error {\n\n}\n"
  },
  {
    "path": "Tests/ContainerizationArchiveTests/ArchiveReaderTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport SystemPackage\nimport Testing\n\n@testable import ContainerizationArchive\n\nstruct ArchiveReaderTests {\n    // MARK: - Helper Methods\n\n    func createTestArchive(name: String, entries: [(path: String, type: EntryType, target: String?)]) throws -> URL {\n        let testDirectory = createTemporaryDirectory(baseName: \"ArchiveReaderTests\")!\n        let archiveURL = testDirectory.appendingPathComponent(\"\\(name).tar\")\n\n        let archiver = try ArchiveWriter(format: .paxRestricted, filter: .none, file: archiveURL)\n\n        for entry in entries {\n            let writeEntry = WriteEntry()\n            writeEntry.path = entry.path\n            writeEntry.permissions = 0o644\n            writeEntry.owner = 1000\n            writeEntry.group = 1000\n\n            switch entry.type {\n            case .regular(let content):\n                writeEntry.fileType = .regular\n                let data = content.data(using: .utf8)!\n                writeEntry.size = numericCast(data.count)\n                try archiver.writeEntry(entry: writeEntry, data: data)\n            case .directory:\n                writeEntry.fileType = .directory\n                writeEntry.permissions = 0o755\n                writeEntry.size = 0\n                try archiver.writeEntry(entry: writeEntry, data: nil)\n            case .symlink:\n                guard let target = entry.target else {\n                    throw ArchiveError.failedToExtractArchive(\"symlink requires target\")\n                }\n                writeEntry.fileType = .symbolicLink\n                writeEntry.symlinkTarget = target\n                writeEntry.size = 0\n                try archiver.writeEntry(entry: writeEntry, data: nil)\n            }\n        }\n\n        try archiver.finishEncoding()\n        return archiveURL\n    }\n\n    func createExtractionDirectory(name: String) throws -> URL {\n        let testDirectory = createTemporaryDirectory(baseName: \"ArchiveReaderTests.\\(name)\")!\n        return testDirectory.appendingPathComponent(\"extract\")\n    }\n\n    enum EntryType {\n        case regular(String)  // Content\n        case directory\n        case symlink\n    }\n\n    // MARK: - Benign Archive Tests\n\n    @Test func extractBenignArchive() throws {\n        let archiveURL = try createTestArchive(\n            name: \"benign\",\n            entries: [\n                (\"dir/\", .directory, nil),\n                (\"dir/file.txt\", .regular(\"test content\"), nil),\n                (\"dir/subdir/\", .directory, nil),\n                (\"dir/subdir/file2.txt\", .regular(\"more content\"), nil),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"benign\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        #expect(rejectedPaths.isEmpty, \"Benign archive should not reject any entries\")\n\n        // Verify files were extracted\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"dir/file.txt\").path))\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"dir/subdir/file2.txt\").path))\n\n        // Verify content\n        let content1 = try String(contentsOf: extractDir.appendingPathComponent(\"dir/file.txt\"), encoding: .utf8)\n        #expect(content1 == \"test content\")\n\n        let content2 = try String(contentsOf: extractDir.appendingPathComponent(\"dir/subdir/file2.txt\"), encoding: .utf8)\n        #expect(content2 == \"more content\")\n    }\n\n    @Test func extractRootLevelFile() throws {\n        let archiveURL = try createTestArchive(\n            name: \"root-level\",\n            entries: [\n                (\"file.txt\", .regular(\"root file\"), nil)\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"root-level\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        #expect(rejectedPaths.isEmpty)\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"file.txt\").path))\n\n        let content = try String(contentsOf: extractDir.appendingPathComponent(\"file.txt\"), encoding: .utf8)\n        #expect(content == \"root file\")\n    }\n\n    // MARK: - Absolute Path Tests\n\n    @Test func convertAbsolutePathToRelative() throws {\n        let filename1: String = \"/tmp/\\(UUID())\"\n        let filename2: String = \"//tmp//\\(UUID())\"\n        let archiveURL = try createTestArchive(\n            name: \"benign-absolute\",\n            entries: [\n                (\"/tmp/\\(filename1)\", .regular(\"hello\"), nil),\n                (\"//tmp//\\(filename2)\", .regular(\"world\"), nil),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"benign-absolute\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        // Absolute paths should be rejected\n        #expect(\n            rejectedPaths.isEmpty,\n            \"Expected absolute paths allowed, but got rejected paths \\(rejectedPaths)\")\n\n        // Verify nothing was extracted to /tmp or /etc\n        #expect(!FileManager.default.fileExists(atPath: filename1))\n        #expect(!FileManager.default.fileExists(atPath: filename2))\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"tmp/\\(filename1)\").path))\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"tmp/\\(filename2)\").path))\n    }\n\n    // MARK: - Path Traversal Attack Tests\n\n    @Test func rejectPathTraversal() throws {\n        let archiveURL = try createTestArchive(\n            name: \"evil-traversal\",\n            entries: [\n                (\"../etc/pwned\", .regular(\"evil\"), nil),\n                (\"foo/../../etc/pwned\", .regular(\"evil\"), nil),\n                (\"dir/../../../etc/pwned\", .regular(\"evil\"), nil),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"evil-traversal\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        // Path traversal entries should be rejected\n        #expect(\n            Set(rejectedPaths) == Set([\"../etc/pwned\", \"foo/../../etc/pwned\", \"dir/../../../etc/pwned\"]),\n            \"Expected path traversal entries to be rejected, got \\(rejectedPaths)\")\n\n        // Verify nothing escaped\n        let parentDir = extractDir.deletingLastPathComponent()\n        #expect(!FileManager.default.fileExists(atPath: parentDir.appendingPathComponent(\"etc/pwned\").path))\n    }\n\n    @Test func rejectPathTraversalWithValidEntries() throws {\n        let archiveURL = try createTestArchive(\n            name: \"mixed-traversal\",\n            entries: [\n                (\"safe.txt\", .regular(\"safe content\"), nil),\n                (\"dir/\", .directory, nil),\n                (\"dir/file.txt\", .regular(\"also safe\"), nil),\n                (\"../etc/pwned\", .regular(\"evil\"), nil),\n                (\"more/safe.txt\", .regular(\"still safe\"), nil),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"mixed-traversal\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        // Only the path traversal entry should be rejected\n        #expect(\n            rejectedPaths == [\"../etc/pwned\"],\n            \"Expected only path traversal entry to be rejected, got \\(rejectedPaths)\")\n\n        // Valid entries should have been extracted\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"safe.txt\").path))\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"dir/file.txt\").path))\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"more/safe.txt\").path))\n\n        // Verify nothing escaped\n        let parentDir = extractDir.deletingLastPathComponent()\n        #expect(!FileManager.default.fileExists(atPath: parentDir.appendingPathComponent(\"etc/pwned\").path))\n    }\n\n    @Test func rejectDotDotInMiddle() throws {\n        let archiveURL = try createTestArchive(\n            name: \"evil-dotdot-middle\",\n            entries: [\n                (\"safe/../pwned.txt\", .regular(\"evil\"), nil)\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"evil-dotdot-middle\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        #expect(rejectedPaths == [\"safe/../pwned.txt\"])\n        #expect(!FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"pwned.txt\").path))\n    }\n\n    // MARK: - Symlink Attack Tests\n\n    @Test func allowValidSymlink() throws {\n        let archiveURL = try createTestArchive(\n            name: \"safe-symlink\",\n            entries: [\n                (\"dir/\", .directory, nil),\n                (\"dir/target.txt\", .regular(\"target content\"), nil),\n                (\"dir/link\", .symlink, \"target.txt\"),\n                (\"link2\", .symlink, \"dir/target.txt\"),\n                (\"dir/passwd\", .symlink, \"/etc/passwd\"),\n                (\"dir2/passwd\", .symlink, \"../../../../etc/passwd\"),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"safe-symlink\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        #expect(rejectedPaths.isEmpty, \"Valid symlinks should be allowed\")\n\n        // Verify symlinks were created\n        let linkPath = extractDir.appendingPathComponent(\"dir/link\").path\n        #expect(FileManager.default.fileExists(atPath: linkPath))\n\n        let link2Path = extractDir.appendingPathComponent(\"link2\").path\n        #expect(FileManager.default.fileExists(atPath: link2Path))\n\n        // Verify symlinks point to correct targets\n        let linkTarget = try FileManager.default.destinationOfSymbolicLink(atPath: linkPath)\n        #expect(linkTarget == \"target.txt\")\n\n        let link2Target = try FileManager.default.destinationOfSymbolicLink(atPath: link2Path)\n        #expect(link2Target == \"dir/target.txt\")\n    }\n\n    @Test func allowSymlinkWithDotDot() throws {\n        let archiveURL = try createTestArchive(\n            name: \"safe-symlink-dotdot\",\n            entries: [\n                (\"dir/\", .directory, nil),\n                (\"dir/subdir/\", .directory, nil),\n                (\"target.txt\", .regular(\"target\"), nil),\n                (\"dir/subdir/link\", .symlink, \"../../target.txt\"),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"safe-symlink-dotdot\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        #expect(rejectedPaths.isEmpty, \"Symlink with .. that stays in root should be allowed\")\n\n        let linkPath = extractDir.appendingPathComponent(\"dir/subdir/link\").path\n        #expect(FileManager.default.fileExists(atPath: linkPath))\n\n        let linkTarget = try FileManager.default.destinationOfSymbolicLink(atPath: linkPath)\n        #expect(linkTarget == \"../../target.txt\")\n    }\n\n    // MARK: - Deep Nesting Tests\n\n    @Test func extractDeepNesting() throws {\n        var entries: [(String, EntryType, String?)] = []\n\n        // Create 50 levels deep\n        var path = \"\"\n        for i in 0..<50 {\n            if i > 0 { path += \"/\" }\n            path += \"level\\(i)\"\n            entries.append((path + \"/\", .directory, nil))\n        }\n        entries.append((path + \"/deep.txt\", .regular(\"deep file\"), nil))\n\n        let archiveURL = try createTestArchive(name: \"deep-nesting\", entries: entries)\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"deep-nesting\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        #expect(rejectedPaths.isEmpty)\n\n        // Verify deep file exists\n        let deepFilePath = extractDir.appendingPathComponent(path + \"/deep.txt\").path\n        #expect(FileManager.default.fileExists(atPath: deepFilePath))\n\n        let content = try String(contentsOfFile: deepFilePath, encoding: .utf8)\n        #expect(content == \"deep file\")\n    }\n\n    // MARK: - Normalization Tests\n\n    @Test func handleDotSlashPrefix() throws {\n        let archiveURL = try createTestArchive(\n            name: \"dot-slash\",\n            entries: [\n                (\"./safe.txt\", .regular(\"content\"), nil),\n                (\"./dir/\", .directory, nil),\n                (\"./dir/file.txt\", .regular(\"more content\"), nil),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"dot-slash\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        #expect(rejectedPaths.isEmpty, \"./ prefix should be normalized and allowed\")\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"safe.txt\").path))\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"dir/file.txt\").path))\n    }\n\n    @Test func handleDoubleSlashes() throws {\n        let archiveURL = try createTestArchive(\n            name: \"double-slash\",\n            entries: [\n                (\"dir//subdir/\", .directory, nil),\n                (\"dir//subdir//file.txt\", .regular(\"content\"), nil),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"double-slash\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        #expect(rejectedPaths.isEmpty, \"Double slashes should be normalized\")\n\n        // Verify file exists at normalized path\n        let normalizedPath = \"dir/subdir/file.txt\"\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(normalizedPath).path))\n    }\n\n    // MARK: - File Permissions Tests\n\n    @Test func preserveFilePermissions() throws {\n        let archiveURL = try createTestArchive(\n            name: \"permissions\",\n            entries: [\n                (\"executable.sh\", .regular(\"#!/bin/bash\\necho test\"), nil)\n            ])\n\n        // Manually set executable permissions\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        for (entry, _) in reader {\n            if entry.path == \"executable.sh\" {\n                entry.permissions = 0o755\n            }\n        }\n\n        // Re-create archive with proper permissions\n        let testDirectory = createTemporaryDirectory(baseName: \"ArchiveReaderTests\")!\n        let archiveURL2 = testDirectory.appendingPathComponent(\"permissions2.tar\")\n        let archiver = try ArchiveWriter(format: .paxRestricted, filter: .none, file: archiveURL2)\n\n        let writeEntry = WriteEntry()\n        writeEntry.path = \"executable.sh\"\n        writeEntry.fileType = .regular\n        writeEntry.permissions = 0o755\n        let data = \"#!/bin/bash\\necho test\".data(using: .utf8)!\n        writeEntry.size = numericCast(data.count)\n        try archiver.writeEntry(entry: writeEntry, data: data)\n        try archiver.finishEncoding()\n\n        defer { try? FileManager.default.removeItem(at: testDirectory) }\n\n        let extractDir = try createExtractionDirectory(name: \"permissions\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader2 = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL2)\n        let rejectedPaths = try reader2.extractContents(to: extractDir)\n\n        #expect(rejectedPaths.isEmpty)\n\n        // Verify permissions were preserved\n        let filePath = extractDir.appendingPathComponent(\"executable.sh\").path\n        let attrs = try FileManager.default.attributesOfItem(atPath: filePath)\n        let perms = (attrs[.posixPermissions] as? NSNumber)?.uint16Value ?? 0\n        let permMask: UInt16 = 0o777\n        #expect((perms & permMask) == 0o755, \"Permissions should be preserved\")\n    }\n\n    // MARK: - Duplicate Entry Tests\n\n    @Test func duplicateRegularFiles() throws {\n        let archiveURL = try createTestArchive(\n            name: \"duplicate-regular\",\n            entries: [\n                (\"file.txt\", .regular(\"first content\"), nil),\n                (\"file.txt\", .regular(\"second content\"), nil),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"duplicate-regular\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        // Last entry wins - second file should replace first\n        #expect(rejectedPaths.isEmpty, \"Duplicate files follow last-entry-wins\")\n\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"file.txt\").path))\n        let content = try String(contentsOf: extractDir.appendingPathComponent(\"file.txt\"), encoding: .utf8)\n        #expect(content == \"second content\", \"Last entry should win\")\n    }\n\n    @Test func duplicateDirectories() throws {\n        let archiveURL = try createTestArchive(\n            name: \"duplicate-dirs\",\n            entries: [\n                (\"dir/\", .directory, nil),\n                (\"dir/\", .directory, nil),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"duplicate-dirs\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        // Both directories should be accepted (merged)\n        #expect(rejectedPaths.isEmpty, \"Duplicate directories should be merged\")\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"dir\").path))\n    }\n\n    @Test func regularFileToDirectory() throws {\n        let archiveURL = try createTestArchive(\n            name: \"file-to-dir\",\n            entries: [\n                (\"path\", .regular(\"content\"), nil),\n                (\"path/\", .directory, nil),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"file-to-dir\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        // Directory should replace the file\n        #expect(rejectedPaths.isEmpty, \"Directory should replace regular file\")\n\n        let attrs = try FileManager.default.attributesOfItem(atPath: extractDir.appendingPathComponent(\"path\").path)\n        let fileType = attrs[.type] as? FileAttributeType\n        #expect(fileType == .typeDirectory, \"Path should be a directory\")\n    }\n\n    @Test func directoryToRegularFile() throws {\n        let archiveURL = try createTestArchive(\n            name: \"dir-to-file\",\n            entries: [\n                (\"path/\", .directory, nil),\n                (\"path\", .regular(\"content\"), nil),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"dir-to-file\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        // Last entry wins - file should replace directory\n        #expect(rejectedPaths.isEmpty, \"Regular file should replace directory\")\n\n        // Should now be a regular file\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"path\").path))\n        let content = try String(contentsOf: extractDir.appendingPathComponent(\"path\"), encoding: .utf8)\n        #expect(content == \"content\", \"Should have file content\")\n    }\n\n    @Test func regularFileToSymlink() throws {\n        let archiveURL = try createTestArchive(\n            name: \"file-to-symlink\",\n            entries: [\n                (\"target.txt\", .regular(\"target\"), nil),\n                (\"path\", .regular(\"content\"), nil),\n                (\"path\", .symlink, \"target.txt\"),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"file-to-symlink\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        // Last entry wins - symlink should replace file\n        #expect(rejectedPaths.isEmpty, \"Symlink should replace regular file\")\n\n        // Should now be a symlink\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"path\").path))\n        let linkTarget = try FileManager.default.destinationOfSymbolicLink(atPath: extractDir.appendingPathComponent(\"path\").path)\n        #expect(linkTarget == \"target.txt\")\n    }\n\n    @Test func symlinkToRegularFile() throws {\n        let archiveURL = try createTestArchive(\n            name: \"symlink-to-file\",\n            entries: [\n                (\"target.txt\", .regular(\"target\"), nil),\n                (\"path\", .symlink, \"target.txt\"),\n                (\"path\", .regular(\"new content\"), nil),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"symlink-to-file\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        // Last entry wins - file should replace symlink\n        #expect(rejectedPaths.isEmpty, \"Regular file should replace symlink\")\n\n        // Should now be a regular file\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"path\").path))\n        let content = try String(contentsOf: extractDir.appendingPathComponent(\"path\"), encoding: .utf8)\n        #expect(content == \"new content\")\n    }\n\n    @Test func symlinkToDirectory() throws {\n        let archiveURL = try createTestArchive(\n            name: \"symlink-to-dir\",\n            entries: [\n                (\"target/\", .directory, nil),\n                (\"path\", .symlink, \"target\"),\n                (\"path/\", .directory, nil),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"symlink-to-dir\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        // Directory should replace symlink\n        #expect(rejectedPaths.isEmpty, \"Directory should replace symlink\")\n\n        // Path should now be a directory\n        let attrs = try FileManager.default.attributesOfItem(atPath: extractDir.appendingPathComponent(\"path\").path)\n        let fileType = attrs[.type] as? FileAttributeType\n        #expect(fileType == .typeDirectory, \"Path should be a directory\")\n    }\n\n    @Test func duplicateSymlinks() throws {\n        let archiveURL = try createTestArchive(\n            name: \"duplicate-symlinks\",\n            entries: [\n                (\"target1.txt\", .regular(\"target1\"), nil),\n                (\"target2.txt\", .regular(\"target2\"), nil),\n                (\"link\", .symlink, \"target1.txt\"),\n                (\"link\", .symlink, \"target2.txt\"),\n            ])\n\n        defer { try? FileManager.default.removeItem(at: archiveURL.deletingLastPathComponent()) }\n\n        let extractDir = try createExtractionDirectory(name: \"duplicate-symlinks\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        // Last entry wins - second symlink should replace first\n        #expect(rejectedPaths.isEmpty, \"Second symlink should replace first\")\n\n        let linkTarget = try FileManager.default.destinationOfSymbolicLink(atPath: extractDir.appendingPathComponent(\"link\").path)\n        #expect(linkTarget == \"target2.txt\", \"Last symlink should win\")\n    }\n\n    // MARK: - Empty Archive Tests\n\n    @Test func rejectEmptyArchive() throws {\n        let testDirectory = createTemporaryDirectory(baseName: \"ArchiveReaderTests\")!\n        let archiveURL = testDirectory.appendingPathComponent(\"empty.tar\")\n\n        let archiver = try ArchiveWriter(format: .paxRestricted, filter: .none, file: archiveURL)\n        try archiver.finishEncoding()\n\n        defer { try? FileManager.default.removeItem(at: testDirectory) }\n\n        let extractDir = try createExtractionDirectory(name: \"empty\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .none, file: archiveURL)\n\n        #expect(throws: ArchiveError.self) {\n            _ = try reader.extractContents(to: extractDir)\n        }\n    }\n\n    // MARK: - Zstd Compression Tests\n\n    @Test func readZstdCompressedArchive() throws {\n        guard let resourceURL = Bundle.module.url(forResource: \"test\", withExtension: \"tar.zst\") else {\n            Issue.record(\"Test resource test.tar.zst not found\")\n            return\n        }\n\n        let extractDir = try createExtractionDirectory(name: \"zstd-test\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        // Test with explicit filter\n        let reader = try ArchiveReader(format: .paxRestricted, filter: .zstd, file: resourceURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        #expect(rejectedPaths.isEmpty, \"No paths should be rejected\")\n\n        // Check extracted files\n        let testFile = extractDir.appendingPathComponent(\"test.txt\")\n        let file2 = extractDir.appendingPathComponent(\"file2.txt\")\n\n        #expect(FileManager.default.fileExists(atPath: testFile.path), \"test.txt should exist\")\n        #expect(FileManager.default.fileExists(atPath: file2.path), \"file2.txt should exist\")\n\n        let testContent = try String(contentsOf: testFile, encoding: .utf8)\n        #expect(testContent == \"Hello from zstd compressed archive\", \"Content should match\")\n\n        let file2Content = try String(contentsOf: file2, encoding: .utf8)\n        #expect(file2Content == \"Another file\", \"Content should match\")\n    }\n\n    @Test func readZstdCompressedArchiveAutoDetect() throws {\n        guard let resourceURL = Bundle.module.url(forResource: \"test\", withExtension: \"tar.zst\") else {\n            Issue.record(\"Test resource test.tar.zst not found\")\n            return\n        }\n\n        let extractDir = try createExtractionDirectory(name: \"zstd-auto-test\")\n        defer { try? FileManager.default.removeItem(at: extractDir.deletingLastPathComponent()) }\n\n        // Test with auto-detect\n        let reader = try ArchiveReader(file: resourceURL)\n        let rejectedPaths = try reader.extractContents(to: extractDir)\n\n        #expect(rejectedPaths.isEmpty, \"No paths should be rejected\")\n\n        // Check extracted files\n        let testFile = extractDir.appendingPathComponent(\"test.txt\")\n        #expect(FileManager.default.fileExists(atPath: testFile.path), \"test.txt should exist\")\n\n        let testContent = try String(contentsOf: testFile, encoding: .utf8)\n        #expect(testContent == \"Hello from zstd compressed archive\", \"Content should match\")\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationArchiveTests/ArchiveTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationArchive\n\nstruct ArchiveTests {\n    func helperEntry(path: String, data: Data) -> WriteEntry {\n        let entry = WriteEntry()\n        entry.permissions = 0o644\n        entry.fileType = .regular\n        entry.path = path\n        entry.size = numericCast(data.count)\n        entry.owner = 1\n        entry.group = 2\n        entry.xattrs = [\"user.data\": Data([1, 2, 3])]\n        return entry\n    }\n\n    @Test func createTemporaryDirectorySuccess() throws {\n        // Test that createTemporaryDirectory creates a directory with randomized suffix\n        let baseName = \"ArchiveTests.testTempDir\"\n        guard let tempDir = createTemporaryDirectory(baseName: baseName) else {\n            Issue.record(\"createTemporaryDirectory returned nil\")\n            return\n        }\n\n        defer {\n            let fileManager = FileManager.default\n            try? fileManager.removeItem(at: tempDir)\n        }\n\n        // Verify the directory exists\n        var isDirectory: ObjCBool = false\n        let fileManager = FileManager.default\n        let exists = fileManager.fileExists(atPath: tempDir.path, isDirectory: &isDirectory)\n        #expect(exists)\n        #expect(isDirectory.boolValue)\n\n        // Verify the directory name starts with the base name\n        let lastComponent = tempDir.lastPathComponent\n        #expect(lastComponent.starts(with: baseName))\n\n        // Verify that mkdtemp replaced the X's with random characters\n        // (should be 6 random alphanumeric characters after the base name and dot)\n        let suffix = String(lastComponent.dropFirst(baseName.count + 1))  // +1 for the dot\n        #expect(suffix.count == 6, \"Expected 6 character suffix, got \\(suffix.count)\")\n        #expect(suffix != \"XXXXXX\", \"mkdtemp did not replace X's with random characters\")\n\n        // Verify we can write to the directory\n        let testFile = tempDir.appendingPathComponent(\"test.txt\")\n        try \"test content\".write(toFile: testFile.path, atomically: true, encoding: .utf8)\n        #expect(fileManager.fileExists(atPath: testFile.path))\n    }\n\n    @Test func tarUTF8() throws {\n        let testDirectory = createTemporaryDirectory(baseName: \"ArchiveTests.testTarUTF8\")!\n        let archiveURL = testDirectory.appendingPathComponent(\"test.tgz\")\n\n        defer {\n            let fileManager = FileManager.default\n            try? fileManager.removeItem(at: testDirectory)\n        }\n\n        // this test would failed with ArchiveWriterConfiguration.locale was not set to \"en_US.UTF-8\"\n        let archiver = try ArchiveWriter(format: .paxRestricted, filter: .gzip, file: archiveURL)\n\n        let data = \"blablabla\".data(using: .utf8)!\n\n        let normalPathEntry = helperEntry(path: \"r\", data: data)\n        #expect(throws: Never.self) {\n            try archiver.writeEntry(entry: normalPathEntry, data: data)\n        }\n\n        let weirdPathEntry = helperEntry(path: \"ʀ\", data: data)\n        #expect(throws: Never.self) {\n            try archiver.writeEntry(entry: weirdPathEntry, data: data)\n        }\n    }\n\n    @Test func tarGzipWithOpenfile() throws {\n        let testDirectory = createTemporaryDirectory(baseName: \"ArchiveTests.testTarGzipWithOpenfile\")!\n        let archiveURL = testDirectory.appendingPathComponent(\"test.tgz\")\n\n        defer {\n            let fileManager = FileManager.default\n            try? fileManager.removeItem(at: testDirectory)\n        }\n\n        let configuration = ArchiveWriterConfiguration(\n            format: .paxRestricted,\n            filter: .gzip\n        )\n        let archiver = try ArchiveWriter(configuration: configuration)\n        try archiver.open(file: archiveURL)\n\n        let data = \"foo\".data(using: .utf8)!\n\n        let normalPathEntry = helperEntry(path: \"bar\", data: data)\n        #expect(throws: Never.self) {\n            try archiver.writeEntry(entry: normalPathEntry, data: data)\n        }\n\n        try archiver.finishEncoding()\n    }\n\n    @Test func writingZip() throws {\n        let testDirectory = createTemporaryDirectory(baseName: \"ArchiveTests.testWritingZip\")!\n        let archiveURL = testDirectory.appendingPathComponent(\"test.zip\")\n\n        defer {\n            let fileManager = FileManager.default\n            try? fileManager.removeItem(at: testDirectory)\n        }\n\n        // When\n        let archiver = try ArchiveWriter(format: .zip, filter: .none, file: archiveURL)\n\n        var data = \"foo\".data(using: .utf8)!\n        var entry = helperEntry(path: \"foo.txt\", data: data)\n        try archiver.writeEntry(entry: entry, data: data)\n\n        data = \"bar\".data(using: .utf8)!\n        entry = helperEntry(path: \"bar.txt\", data: data)\n        try archiver.writeEntry(entry: entry, data: data)\n\n        data = Data()\n        entry = helperEntry(path: \"empty\", data: data)\n        try archiver.writeEntry(entry: entry, data: data)\n\n        try archiver.finishEncoding()\n\n        // Then\n        let unarchiver = try ArchiveReader(format: .zip, filter: .none, file: archiveURL)\n        for (index, (entry, data)) in unarchiver.enumerated() {\n            #expect(entry.owner == 1)\n            #expect(entry.group == 2)\n            switch index {\n            case 0:\n                #expect(entry.path == \"foo.txt\")\n                #expect(String(data: data, encoding: .utf8) == \"foo\")\n            case 1:\n                #expect(entry.path == \"bar.txt\")\n                #expect(String(data: data, encoding: .utf8) == \"bar\")\n            case 2:\n                #expect(entry.path == \"empty\")\n                #expect(data.isEmpty)\n            default:\n                Issue.record()\n            }\n        }\n    }\n\n    @Test func unarchiving_0bytesEntry() throws {\n        let data = Data(base64Encoded: surveyBundleBase64Encoded)!\n        let unarchiver = try ArchiveReader(name: \"survey.zip\", bundle: data)\n        for (index, (entry, data)) in unarchiver.enumerated() {\n            switch index {\n            case 0:\n                #expect(entry.path == \"healthinvolvement.js\")\n                #expect(!data.isEmpty)\n            case 1:\n                #expect(entry.path == \"__MACOSX/\")\n                #expect(data.isEmpty)\n            case 2:\n                #expect(entry.path == \"__MACOSX/._healthinvolvement.js\")\n                #expect(!data.isEmpty)\n            default:\n                Issue.record()\n            }\n        }\n    }\n\n    @Test func writingReadingTar() throws {\n        let testDirectory = createTemporaryDirectory(baseName: \"ArchiveTests.testWritingReadingTar\")!\n        let archiveURL = testDirectory.appendingPathComponent(\"test.tar.gz\")\n        defer {\n            let fileManager = FileManager.default\n            try? fileManager.removeItem(at: testDirectory)\n        }\n\n        let archiver = try ArchiveWriter(format: .pax, filter: .gzip, file: archiveURL)\n        let data = \"foo\".data(using: .utf8)!\n        let entry = helperEntry(path: \"foo.txt\", data: data)\n        try archiver.writeEntry(entry: entry, data: data)\n        try archiver.finishEncoding()\n\n        let unarchiver = try ArchiveReader(format: .pax, filter: .gzip, file: archiveURL)\n        for (entry, _) in unarchiver {\n            let attrs = entry.xattrs\n            guard let val = attrs[\"user.data\"] else {\n                Issue.record(\"missing extended attribute [user.data] in file\")\n                return\n            }\n            #expect([UInt8](val) == [1, 2, 3])\n        }\n    }\n\n    // MARK: - archiveDirectory round-trip tests\n\n    @Test func archiveDirectoryBasic() throws {\n        let testDir = createTemporaryDirectory(baseName: \"ArchiveTests.archiveDirBasic\")!\n        defer { try? FileManager.default.removeItem(at: testDir) }\n\n        let sourceDir = testDir.appendingPathComponent(\"source\")\n        try FileManager.default.createDirectory(at: sourceDir, withIntermediateDirectories: true)\n        try \"hello\".write(to: sourceDir.appendingPathComponent(\"file1.txt\"), atomically: true, encoding: .utf8)\n\n        let subDir = sourceDir.appendingPathComponent(\"subdir\")\n        try FileManager.default.createDirectory(at: subDir, withIntermediateDirectories: true)\n        try \"world\".write(to: subDir.appendingPathComponent(\"file2.txt\"), atomically: true, encoding: .utf8)\n\n        let archiveURL = testDir.appendingPathComponent(\"test.tar.gz\")\n        let writer = try ArchiveWriter(format: .pax, filter: .gzip, file: archiveURL)\n        try writer.archiveDirectory(sourceDir)\n        try writer.finishEncoding()\n\n        let extractDir = testDir.appendingPathComponent(\"extract\")\n        let reader = try ArchiveReader(file: archiveURL)\n        let rejected = try reader.extractContents(to: extractDir)\n\n        #expect(rejected.isEmpty)\n        #expect(try String(contentsOf: extractDir.appendingPathComponent(\"file1.txt\"), encoding: .utf8) == \"hello\")\n        #expect(try String(contentsOf: extractDir.appendingPathComponent(\"subdir/file2.txt\"), encoding: .utf8) == \"world\")\n    }\n\n    @Test func archiveDirectoryEmpty() throws {\n        let testDir = createTemporaryDirectory(baseName: \"ArchiveTests.archiveDirEmpty\")!\n        defer { try? FileManager.default.removeItem(at: testDir) }\n\n        let sourceDir = testDir.appendingPathComponent(\"empty\")\n        try FileManager.default.createDirectory(at: sourceDir, withIntermediateDirectories: true)\n\n        let archiveURL = testDir.appendingPathComponent(\"test.tar.gz\")\n        let writer = try ArchiveWriter(format: .pax, filter: .gzip, file: archiveURL)\n        try writer.archiveDirectory(sourceDir)\n        try writer.finishEncoding()\n\n        let extractDir = testDir.appendingPathComponent(\"extract\")\n        let reader = try ArchiveReader(file: archiveURL)\n        // Empty directory archive should succeed with the leading \"./\" entry.\n        let rejected = try reader.extractContents(to: extractDir)\n        #expect(rejected.isEmpty)\n        #expect(FileManager.default.fileExists(atPath: extractDir.path))\n    }\n\n    @Test func archiveDirectoryNestedEmpty() throws {\n        let testDir = createTemporaryDirectory(baseName: \"ArchiveTests.archiveDirNestedEmpty\")!\n        defer { try? FileManager.default.removeItem(at: testDir) }\n\n        let sourceDir = testDir.appendingPathComponent(\"source\")\n        try FileManager.default.createDirectory(at: sourceDir, withIntermediateDirectories: true)\n        try FileManager.default.createDirectory(\n            at: sourceDir.appendingPathComponent(\"a/b/c\"), withIntermediateDirectories: true)\n        try FileManager.default.createDirectory(\n            at: sourceDir.appendingPathComponent(\"empty\"), withIntermediateDirectories: true)\n        try \"data\".write(to: sourceDir.appendingPathComponent(\"a/file.txt\"), atomically: true, encoding: .utf8)\n\n        let archiveURL = testDir.appendingPathComponent(\"test.tar.gz\")\n        let writer = try ArchiveWriter(format: .pax, filter: .gzip, file: archiveURL)\n        try writer.archiveDirectory(sourceDir)\n        try writer.finishEncoding()\n\n        let extractDir = testDir.appendingPathComponent(\"extract\")\n        let reader = try ArchiveReader(file: archiveURL)\n        let rejected = try reader.extractContents(to: extractDir)\n\n        #expect(rejected.isEmpty)\n        #expect(try String(contentsOf: extractDir.appendingPathComponent(\"a/file.txt\"), encoding: .utf8) == \"data\")\n\n        var isDir: ObjCBool = false\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"a/b/c\").path, isDirectory: &isDir))\n        #expect(isDir.boolValue)\n        #expect(FileManager.default.fileExists(atPath: extractDir.appendingPathComponent(\"empty\").path, isDirectory: &isDir))\n        #expect(isDir.boolValue)\n    }\n\n    @Test func archiveDirectoryDeepNesting() throws {\n        let testDir = createTemporaryDirectory(baseName: \"ArchiveTests.archiveDirDeep\")!\n        defer { try? FileManager.default.removeItem(at: testDir) }\n\n        let sourceDir = testDir.appendingPathComponent(\"source\")\n        var deepPath = sourceDir\n        for i in 0..<20 {\n            deepPath = deepPath.appendingPathComponent(\"level\\(i)\")\n        }\n        try FileManager.default.createDirectory(at: deepPath, withIntermediateDirectories: true)\n        try \"deep content\".write(to: deepPath.appendingPathComponent(\"deep.txt\"), atomically: true, encoding: .utf8)\n\n        let archiveURL = testDir.appendingPathComponent(\"test.tar.gz\")\n        let writer = try ArchiveWriter(format: .pax, filter: .gzip, file: archiveURL)\n        try writer.archiveDirectory(sourceDir)\n        try writer.finishEncoding()\n\n        let extractDir = testDir.appendingPathComponent(\"extract\")\n        let reader = try ArchiveReader(file: archiveURL)\n        let rejected = try reader.extractContents(to: extractDir)\n\n        #expect(rejected.isEmpty)\n\n        var expectedPath = extractDir\n        for i in 0..<20 {\n            expectedPath = expectedPath.appendingPathComponent(\"level\\(i)\")\n        }\n        #expect(try String(contentsOf: expectedPath.appendingPathComponent(\"deep.txt\"), encoding: .utf8) == \"deep content\")\n    }\n\n    @Test func archiveDirectorySymlinkInside() throws {\n        let testDir = createTemporaryDirectory(baseName: \"ArchiveTests.archiveDirSymlinkInside\")!\n        defer { try? FileManager.default.removeItem(at: testDir) }\n\n        let sourceDir = testDir.appendingPathComponent(\"source\")\n        try FileManager.default.createDirectory(at: sourceDir, withIntermediateDirectories: true)\n        try \"target content\".write(to: sourceDir.appendingPathComponent(\"target.txt\"), atomically: true, encoding: .utf8)\n        try FileManager.default.createSymbolicLink(\n            atPath: sourceDir.appendingPathComponent(\"link.txt\").path,\n            withDestinationPath: \"target.txt\"\n        )\n\n        let archiveURL = testDir.appendingPathComponent(\"test.tar.gz\")\n        let writer = try ArchiveWriter(format: .pax, filter: .gzip, file: archiveURL)\n        try writer.archiveDirectory(sourceDir)\n        try writer.finishEncoding()\n\n        let extractDir = testDir.appendingPathComponent(\"extract\")\n        let reader = try ArchiveReader(file: archiveURL)\n        let rejected = try reader.extractContents(to: extractDir)\n\n        #expect(rejected.isEmpty)\n        #expect(try String(contentsOf: extractDir.appendingPathComponent(\"target.txt\"), encoding: .utf8) == \"target content\")\n\n        let linkDest = try FileManager.default.destinationOfSymbolicLink(atPath: extractDir.appendingPathComponent(\"link.txt\").path)\n        #expect(linkDest == \"target.txt\")\n    }\n\n    @Test func archiveDirectorySymlinkOutsideExcluded() throws {\n        let testDir = createTemporaryDirectory(baseName: \"ArchiveTests.archiveDirSymlinkOutside\")!\n        defer { try? FileManager.default.removeItem(at: testDir) }\n\n        let sourceDir = testDir.appendingPathComponent(\"source\")\n        try FileManager.default.createDirectory(at: sourceDir, withIntermediateDirectories: true)\n        try \"inside\".write(to: sourceDir.appendingPathComponent(\"file.txt\"), atomically: true, encoding: .utf8)\n\n        // Create a file outside the source directory\n        try \"outside\".write(to: testDir.appendingPathComponent(\"outside.txt\"), atomically: true, encoding: .utf8)\n\n        // Symlink pointing outside the source directory\n        try FileManager.default.createSymbolicLink(\n            atPath: sourceDir.appendingPathComponent(\"escape.txt\").path,\n            withDestinationPath: \"../outside.txt\"\n        )\n\n        let archiveURL = testDir.appendingPathComponent(\"test.tar.gz\")\n        let writer = try ArchiveWriter(format: .pax, filter: .gzip, file: archiveURL)\n        try writer.archiveDirectory(sourceDir)\n        try writer.finishEncoding()\n\n        // Verify the archive doesn't contain the escaping symlink\n        let reader = try ArchiveReader(file: archiveURL)\n        var paths: [String] = []\n        for (entry, _) in reader {\n            if let path = entry.path {\n                paths.append(path)\n            }\n        }\n        #expect(!paths.contains(\"escape.txt\"), \"Symlink pointing outside should be excluded from archive\")\n        #expect(paths.contains(\"file.txt\"), \"Regular file should be included\")\n    }\n\n    @Test func archiveDirectorySpecialCharacters() throws {\n        let testDir = createTemporaryDirectory(baseName: \"ArchiveTests.archiveDirSpecialChars\")!\n        defer { try? FileManager.default.removeItem(at: testDir) }\n\n        let sourceDir = testDir.appendingPathComponent(\"source\")\n        try FileManager.default.createDirectory(at: sourceDir, withIntermediateDirectories: true)\n        try \"spaces\".write(to: sourceDir.appendingPathComponent(\"file with spaces.txt\"), atomically: true, encoding: .utf8)\n        try \"unicode\".write(to: sourceDir.appendingPathComponent(\"日本語.txt\"), atomically: true, encoding: .utf8)\n        try \"dashes\".write(to: sourceDir.appendingPathComponent(\"file-name_v2.0.txt\"), atomically: true, encoding: .utf8)\n\n        let archiveURL = testDir.appendingPathComponent(\"test.tar.gz\")\n        let writer = try ArchiveWriter(format: .pax, filter: .gzip, file: archiveURL)\n        try writer.archiveDirectory(sourceDir)\n        try writer.finishEncoding()\n\n        let extractDir = testDir.appendingPathComponent(\"extract\")\n        let reader = try ArchiveReader(file: archiveURL)\n        let rejected = try reader.extractContents(to: extractDir)\n\n        #expect(rejected.isEmpty)\n        #expect(try String(contentsOf: extractDir.appendingPathComponent(\"file with spaces.txt\"), encoding: .utf8) == \"spaces\")\n        #expect(try String(contentsOf: extractDir.appendingPathComponent(\"日本語.txt\"), encoding: .utf8) == \"unicode\")\n        #expect(try String(contentsOf: extractDir.appendingPathComponent(\"file-name_v2.0.txt\"), encoding: .utf8) == \"dashes\")\n    }\n\n    @Test func archiveDirectoryDotfiles() throws {\n        let testDir = createTemporaryDirectory(baseName: \"ArchiveTests.archiveDirDotfiles\")!\n        defer { try? FileManager.default.removeItem(at: testDir) }\n\n        let sourceDir = testDir.appendingPathComponent(\"source\")\n        try FileManager.default.createDirectory(at: sourceDir, withIntermediateDirectories: true)\n        try \"hidden\".write(to: sourceDir.appendingPathComponent(\".hidden\"), atomically: true, encoding: .utf8)\n        try \"gitignore\".write(to: sourceDir.appendingPathComponent(\".gitignore\"), atomically: true, encoding: .utf8)\n        try \"visible\".write(to: sourceDir.appendingPathComponent(\"visible.txt\"), atomically: true, encoding: .utf8)\n\n        let hiddenDir = sourceDir.appendingPathComponent(\".config\")\n        try FileManager.default.createDirectory(at: hiddenDir, withIntermediateDirectories: true)\n        try \"config\".write(to: hiddenDir.appendingPathComponent(\"settings.json\"), atomically: true, encoding: .utf8)\n\n        let archiveURL = testDir.appendingPathComponent(\"test.tar.gz\")\n        let writer = try ArchiveWriter(format: .pax, filter: .gzip, file: archiveURL)\n        try writer.archiveDirectory(sourceDir)\n        try writer.finishEncoding()\n\n        let extractDir = testDir.appendingPathComponent(\"extract\")\n        let reader = try ArchiveReader(file: archiveURL)\n        let rejected = try reader.extractContents(to: extractDir)\n\n        #expect(rejected.isEmpty)\n        #expect(try String(contentsOf: extractDir.appendingPathComponent(\".hidden\"), encoding: .utf8) == \"hidden\")\n        #expect(try String(contentsOf: extractDir.appendingPathComponent(\".gitignore\"), encoding: .utf8) == \"gitignore\")\n        #expect(try String(contentsOf: extractDir.appendingPathComponent(\"visible.txt\"), encoding: .utf8) == \"visible\")\n        #expect(try String(contentsOf: extractDir.appendingPathComponent(\".config/settings.json\"), encoding: .utf8) == \"config\")\n    }\n\n    @Test func archiveDirectoryPreservesPermissions() throws {\n        let testDir = createTemporaryDirectory(baseName: \"ArchiveTests.archiveDirPerms\")!\n        defer { try? FileManager.default.removeItem(at: testDir) }\n\n        let sourceDir = testDir.appendingPathComponent(\"source\")\n        try FileManager.default.createDirectory(at: sourceDir, withIntermediateDirectories: true)\n\n        let execFile = sourceDir.appendingPathComponent(\"script.sh\")\n        try \"#!/bin/sh\\necho hi\".write(to: execFile, atomically: true, encoding: .utf8)\n        try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: execFile.path)\n\n        let readOnly = sourceDir.appendingPathComponent(\"readonly.txt\")\n        try \"secret\".write(to: readOnly, atomically: true, encoding: .utf8)\n        try FileManager.default.setAttributes([.posixPermissions: 0o444], ofItemAtPath: readOnly.path)\n\n        let archiveURL = testDir.appendingPathComponent(\"test.tar.gz\")\n        let writer = try ArchiveWriter(format: .pax, filter: .gzip, file: archiveURL)\n        try writer.archiveDirectory(sourceDir)\n        try writer.finishEncoding()\n\n        let extractDir = testDir.appendingPathComponent(\"extract\")\n        let reader = try ArchiveReader(file: archiveURL)\n        let rejected = try reader.extractContents(to: extractDir)\n\n        #expect(rejected.isEmpty)\n\n        let execAttrs = try FileManager.default.attributesOfItem(atPath: extractDir.appendingPathComponent(\"script.sh\").path)\n        let execPerms = (execAttrs[.posixPermissions] as? NSNumber)?.uint16Value ?? 0\n        #expect((execPerms & 0o777) == 0o755, \"Executable permissions should be preserved\")\n\n        let roAttrs = try FileManager.default.attributesOfItem(atPath: extractDir.appendingPathComponent(\"readonly.txt\").path)\n        let roPerms = (roAttrs[.posixPermissions] as? NSNumber)?.uint16Value ?? 0\n        #expect((roPerms & 0o777) == 0o444, \"Read-only permissions should be preserved\")\n    }\n\n    @Test func archiveDirectoryLargeFile() throws {\n        let testDir = createTemporaryDirectory(baseName: \"ArchiveTests.archiveDirLargeFile\")!\n        defer { try? FileManager.default.removeItem(at: testDir) }\n\n        let sourceDir = testDir.appendingPathComponent(\"source\")\n        try FileManager.default.createDirectory(at: sourceDir, withIntermediateDirectories: true)\n\n        // 2MB file with repeating pattern\n        let pattern = Data(\"ContainerizationArchiveTestPattern\\n\".utf8)\n        var largeData = Data(capacity: 2 * 1024 * 1024)\n        while largeData.count < 2 * 1024 * 1024 {\n            largeData.append(pattern)\n        }\n        largeData = largeData.prefix(2 * 1024 * 1024)\n        try largeData.write(to: sourceDir.appendingPathComponent(\"large.bin\"))\n\n        let archiveURL = testDir.appendingPathComponent(\"test.tar.gz\")\n        let writer = try ArchiveWriter(format: .pax, filter: .gzip, file: archiveURL)\n        try writer.archiveDirectory(sourceDir)\n        try writer.finishEncoding()\n\n        let extractDir = testDir.appendingPathComponent(\"extract\")\n        let reader = try ArchiveReader(file: archiveURL)\n        let rejected = try reader.extractContents(to: extractDir)\n\n        #expect(rejected.isEmpty)\n\n        let extracted = try Data(contentsOf: extractDir.appendingPathComponent(\"large.bin\"))\n        #expect(extracted == largeData, \"Large file content should match after round-trip\")\n    }\n\n    @Test func archiveDirectoryManyFiles() throws {\n        let testDir = createTemporaryDirectory(baseName: \"ArchiveTests.archiveDirManyFiles\")!\n        defer { try? FileManager.default.removeItem(at: testDir) }\n\n        let sourceDir = testDir.appendingPathComponent(\"source\")\n        try FileManager.default.createDirectory(at: sourceDir, withIntermediateDirectories: true)\n\n        // Create 100 files\n        for i in 0..<100 {\n            try \"content \\(i)\".write(\n                to: sourceDir.appendingPathComponent(\"file_\\(String(format: \"%03d\", i)).txt\"),\n                atomically: true, encoding: .utf8)\n        }\n\n        let archiveURL = testDir.appendingPathComponent(\"test.tar.gz\")\n        let writer = try ArchiveWriter(format: .pax, filter: .gzip, file: archiveURL)\n        try writer.archiveDirectory(sourceDir)\n        try writer.finishEncoding()\n\n        let extractDir = testDir.appendingPathComponent(\"extract\")\n        let reader = try ArchiveReader(file: archiveURL)\n        let rejected = try reader.extractContents(to: extractDir)\n\n        #expect(rejected.isEmpty)\n\n        for i in 0..<100 {\n            let content = try String(\n                contentsOf: extractDir.appendingPathComponent(\"file_\\(String(format: \"%03d\", i)).txt\"),\n                encoding: .utf8)\n            #expect(content == \"content \\(i)\")\n        }\n    }\n\n    @Test func archiveDirectorySingleFile() throws {\n        let testDir = createTemporaryDirectory(baseName: \"ArchiveTests.archiveDirSingle\")!\n        defer { try? FileManager.default.removeItem(at: testDir) }\n\n        let sourceDir = testDir.appendingPathComponent(\"source\")\n        try FileManager.default.createDirectory(at: sourceDir, withIntermediateDirectories: true)\n        try \"only file\".write(to: sourceDir.appendingPathComponent(\"only.txt\"), atomically: true, encoding: .utf8)\n\n        let archiveURL = testDir.appendingPathComponent(\"test.tar.gz\")\n        let writer = try ArchiveWriter(format: .pax, filter: .gzip, file: archiveURL)\n        try writer.archiveDirectory(sourceDir)\n        try writer.finishEncoding()\n\n        let extractDir = testDir.appendingPathComponent(\"extract\")\n        let reader = try ArchiveReader(file: archiveURL)\n        let rejected = try reader.extractContents(to: extractDir)\n\n        #expect(rejected.isEmpty)\n        #expect(try String(contentsOf: extractDir.appendingPathComponent(\"only.txt\"), encoding: .utf8) == \"only file\")\n    }\n\n    @Test func archiveDirectorySymlinkRelativeSubdir() throws {\n        let testDir = createTemporaryDirectory(baseName: \"ArchiveTests.archiveDirSymlinkRelSubdir\")!\n        defer { try? FileManager.default.removeItem(at: testDir) }\n\n        let sourceDir = testDir.appendingPathComponent(\"source\")\n        let subA = sourceDir.appendingPathComponent(\"a\")\n        let subB = sourceDir.appendingPathComponent(\"b\")\n        try FileManager.default.createDirectory(at: subA, withIntermediateDirectories: true)\n        try FileManager.default.createDirectory(at: subB, withIntermediateDirectories: true)\n        try \"in a\".write(to: subA.appendingPathComponent(\"file.txt\"), atomically: true, encoding: .utf8)\n\n        // Symlink from b/link.txt -> ../a/file.txt (relative, stays inside)\n        try FileManager.default.createSymbolicLink(\n            atPath: subB.appendingPathComponent(\"link.txt\").path,\n            withDestinationPath: \"../a/file.txt\"\n        )\n\n        let archiveURL = testDir.appendingPathComponent(\"test.tar.gz\")\n        let writer = try ArchiveWriter(format: .pax, filter: .gzip, file: archiveURL)\n        try writer.archiveDirectory(sourceDir)\n        try writer.finishEncoding()\n\n        let extractDir = testDir.appendingPathComponent(\"extract\")\n        let reader = try ArchiveReader(file: archiveURL)\n        let rejected = try reader.extractContents(to: extractDir)\n\n        #expect(rejected.isEmpty)\n\n        let linkDest = try FileManager.default.destinationOfSymbolicLink(\n            atPath: extractDir.appendingPathComponent(\"b/link.txt\").path)\n        #expect(linkDest == \"../a/file.txt\")\n\n        // Verify the symlink resolves correctly\n        let content = try String(contentsOf: extractDir.appendingPathComponent(\"b/link.txt\"), encoding: .utf8)\n        #expect(content == \"in a\")\n    }\n}\n\nprivate let surveyBundleBase64Encoded = \"\"\"\n    UEsDBBQACAAIAA17o04AAAAAAAAAAAAAAAAUABAAaGVhbHRoaW52b2x2ZW1lbnQuanNVWAwAQ8XMXJm/zFz1ARQAnVVRa9swEH73rzjylMLwmu3NJexhDLqxroPAYIxRFPsca5UlT5LteSH/fSdZdu02LqMiEEl3uvv06e5zwzRwyS1n4q4qmEHYwjECGn6VwKrSXGluu9Urv50rXSbBxWBquZImgR9+/Wgcz226IVTKBP+L2We2R0E5rlULrapFBp2qYY/GQoYm1XyPbsdBbJRosERpaQ7cmBoNaBTMYgZW9V4FMmGLdwBfBbqrpIVS9KckxgH9mXGPHSGYJFh2ZdK0qJPli9mucpTtuDwIfF8onuJytNTbl8ibj+NTzr4oC4x+QgzsZDHcdIGElGmESquGZ6gh45qeykDpyAgeI3s9mcQQNEzUhP8STougn4W0UyW2BbMTQB8h9e9KD5kpogVKRcDowUom2QGhHABP8m9emv8b6m6W29KacrVK3wMzwMAiK6HldPvyPFPPI3vzUmQf/lhNxSXm8FQrH9JQdWVA6JgEljUUwKJrNnIwKPIJiLf/BeLnksvyYW5uK9fPjBDnTBg853h6vNknOkWnKMpr6QUB0EGlC6yxoYa6CA3TkNYMGuMNsV9dRd7Kc1gH63brGtKL0upi0m0aba3lXK9C9mhiP47alVHrrxaw3Wk0FYkXmvU4G5KFQOP+LADVyoEsZn65cOR2/4s6LSZRCfb4IXgsUB7ooV/DxgV0dPymznNBJaMOHaWXKlFannOnNatUlTFLWxRCUtJ4diLuS+epeFluhSPgxtWya7vvTh+vvXdw6QXWP7hTYG/q4BP5SexgV+sGB81vUJvebRNfDt+BQEcyEtrvh5XSyRmme5eBwGScOTqi2c2uon9QSwcIxOijbWkCAACaBgAAUEsDBAoAAAAAAFV+o04AAAAAAAAAAAAAAAAJABAAX19NQUNPU1gvVVgMAMHFzFzBxcxc9QEUAFBLAwQUAAgACAANe6NOAAAAAAAAAAAAAAAAHwAQAF9fTUFDT1NYLy5faGVhbHRoaW52b2x2ZW1lbnQuanNVWAwAQ8XMXJm/zFz1ARQAjY/NSsNAEMcnRfHjVBA9eLGiHjy0m5qkDa2XtGlrwVKxAUUUWZMpiW4+mmzrxZtP4pOINw8efQXx6BMIbmigUBAd2P/MDr8/MwOLG0uQA+hRu9AfFM4LWaQ9WBHvAED6Eln8c9vwrzAs63RapQ5pVxTfc8hC1s8DbNqhX6JRxLDEaMLHCToO5bhzMpiikipEA9ifcT5yKhhau+uZXY6+Gd4HLKQOOqZwph5PyAPA3u+eMxdjbMehn6T8h5BDgPUZPxrTmAbcCxDen98u002cz9dymm8i5iVclp8kxXghV7j1eLO8mi0rZQfm5g5em5mu8z2X8yipERJhFGFsu5RnU8V8MvQYJqRMNLVK/LDV71iMWW583OyY5Agp4243mIRsgj4GvHSb/Dn7YkRkWVfqmk1RV3RaH9Ahjb16y6hUKxVNK8rtcqOo6kIastooNlXT1IyWoTYVA34AUEsHCAK+cV1ZAQAAIQIAAFBLAQIVAxQACAAIAA17o07E6KNtaQIAAJoGAAAUAAwAAAAAAAAAAECkgQAAAABoZWFsdGhpbnZvbHZlbWVudC5qc1VYCABDxcxcmb/MXFBLAQIVAwoAAAAAAFV+o04AAAAAAAAAAAAAAAAJAAwAAAAAAAAAAED9QbsCAABfX01BQ09TWC9VWAgAwcXMXMHFzFxQSwECFQMUAAgACAANe6NOAr5xXVkBAAAhAgAAHwAMAAAAAAAAAABApIHyAgAAX19NQUNPU1gvLl9oZWFsdGhpbnZvbHZlbWVudC5qc1VYCABDxcxcmb/MXFBLBQYAAAAAAwADAOoAAACoBAAAAAA=\n    \"\"\"\n"
  },
  {
    "path": "Tests/ContainerizationEXT4Tests/Resources/content/blobs/sha256/48a06049d3738991b011ca8b12473d712b7c40666a1462118dae3c403676afc2",
    "content": "{\n  \"schemaVersion\": 2,\n  \"mediaType\": \"application/vnd.oci.image.manifest.v1+json\",\n  \"config\": {\n    \"mediaType\": \"application/vnd.oci.image.config.v1+json\",\n    \"digest\": \"sha256:8e2eb240a6cd7be1a0d308125afe0060b020e89275ced2e729eda7d4eeff62a2\",\n    \"size\": 824\n  },\n  \"layers\": [\n    {\n      \"mediaType\": \"application/vnd.oci.image.layer.v1.tar+gzip\",\n      \"digest\": \"sha256:c6b39de5b33961661dc939b997cc1d30cda01e38005a6c6625fd9c7e748bab44\",\n      \"size\": 3333361\n    },\n    {\n      \"mediaType\": \"application/vnd.oci.image.layer.v1.tar+gzip\",\n      \"digest\": \"sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1\",\n      \"size\": 32\n    }\n  ]\n}"
  },
  {
    "path": "Tests/ContainerizationEXT4Tests/Resources/content/blobs/sha256/8e2eb240a6cd7be1a0d308125afe0060b020e89275ced2e729eda7d4eeff62a2",
    "content": "{\"architecture\":\"arm64\",\"config\":{\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"/bin/sh\"],\"OnBuild\":null},\"created\":\"2024-03-16T00:09:03.929767682Z\",\"history\":[{\"created\":\"2024-01-26T23:44:55.650290626Z\",\"created_by\":\"/bin/sh -c #(nop) ADD file:6dc287a22d6cc7723b0576dd3a9a644468d133c54d42c8a8eda403e3117648f7 in / \"},{\"created\":\"2024-01-26T23:44:55.750082605Z\",\"created_by\":\"/bin/sh -c #(nop)  CMD [\\\"/bin/sh\\\"]\",\"empty_layer\":true},{\"created\":\"2024-03-16T00:09:03.929767682Z\",\"created_by\":\"RUN /bin/sh -c echo \\\"test\\\" # buildkit\",\"comment\":\"buildkit.dockerfile.v0\"}],\"os\":\"linux\",\"rootfs\":{\"type\":\"layers\",\"diff_ids\":[\"sha256:7c504f21be85c8ade51b7ade32a39a4269bcbcf0e593352923f1b8ea6278e5ef\",\"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef\"]},\"variant\":\"v8\"}"
  },
  {
    "path": "Tests/ContainerizationEXT4Tests/Resources/content/blobs/sha256/ad59e9f71edceca7b1ac7c642410858489b743c97233b0a26a5e2098b1443762",
    "content": "{\"schemaVersion\":2,\"mediaType\":\"application/vnd.oci.image.index.v1+json\",\"manifests\":[{\"mediaType\":\"application/vnd.oci.image.manifest.v1+json\",\"digest\":\"sha256:48a06049d3738991b011ca8b12473d712b7c40666a1462118dae3c403676afc2\",\"size\":667,\"annotations\":{\"com.apple.container.sign.v1.certificate/ptr\":\"mac-Q5W6919KP6:41FB3AB2-E9B9-45CE-8252-8C17C8038670:wlan0\",\"com.apple.container.sign.v1.signature\":\"MEUCIEX3psgFczBpby6sMdzBk5FF5ID5UbqM4nOpqfiVbkseAiEAlDLBr9ajHiswl8/rOyVmYdN98lakuK+dKyABEBXRXeQ=\"},\"platform\":{\"architecture\":\"arm64\",\"os\":\"linux\"}}],\"annotations\":{\"com.apple.container.info.v1.dockerfile-sha256sum\":\"d95983c2a8acbd4cf861c7d3b9117d3e722aebc3768ca682ddf2a427e2fd6583\",\"com.apple.container.sign.v1.certificate/mac-Q5W6919KP6:41FB3AB2-E9B9-45CE-8252-8C17C8038670:wlan0\":\"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURjekNDQXhpZ0F3SUJBZ0lJRW9tR0duRkFGTnd3Q2dZSUtvWkl6ajBFQXdJd2FqRWtNQ0lHQTFVRUF3d2INClFYQndiR1VnUTI5eWNHOXlZWFJsSUZCTFNVNUpWQ0JEUVNBeU1TQXdIZ1lEVlFRTERCZERaWEowYVdacFkyRjANCmFXOXVJRUYxZEdodmNtbDBlVEVUTUJFR0ExVUVDZ3dLUVhCd2JHVWdTVzVqTGpFTE1Ba0dBMVVFQmhNQ1ZWTXcNCkhoY05Nakl4TWpJd01Ua3hOREl3V2hjTk1qVXdNVEU0TVRreE5ERTVXakNCcGpFYU1CZ0dDZ21TSm9tVDhpeGsNCkFRRU1DakkzTURFd09UVTRPVFV4UWpCQUJnTlZCQU1NT1cxaFl5MVJOVmMyT1RFNVMxQTJPalF4UmtJelFVSXkNCkxVVTVRamt0TkRWRFJTMDRNalV5TFRoRE1UZERPREF6T0RZM01EcDNiR0Z1TURFVk1CTUdBMVVFQ3d3TVFYQncNCmJHVkRiMjV1WldOME1STXdFUVlEVlFRS0RBcEJjSEJzWlNCSmJtTXVNUmd3RmdZS0NaSW1pWlB5TEdRQkdSWUkNClNVUk5VeTFUVTA4d1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFTcW5tSjZ3cFluWWlKRkdRaDENCjlOcGkyVnp3M1I3UFlMOXY3MnNiUDZsNy83VUt3YXV4aVk1ZkdEL29yTnhwbWNScFdPRk5mNW1JUWpFcHByMDMNCkpYUXpvNElCYVRDQ0FXVXdEQVlEVlIwVEFRSC9CQUl3QURBZkJnTlZIU01FR0RBV2dCVEx3K0x0QUcxai8rV1QNCkpsLzJJVDVVMEJ6WEZUQkVCZ2dyQmdFRkJRY0JBUVE0TURZd05BWUlLd1lCQlFVSE1BR0dLR2gwZEhBNkx5OXYNClkzTndMbUZ3Y0d4bExtTnZiUzl2WTNOd01ETXRjR3RwYm1sMFkyRXlNREV3VGdZRFZSMFJCRWN3UmFCREJnWXINCkJnRUZBZ0tnT1RBM29CZ2JGa0ZRVUV4RlEwOU9Ua1ZEVkM1QlVGQk1SUzVEVDAyaEd6QVpvQU1DQVFDaEVqQVENCkd3NXphV1JvWVhKMGFHRmZiV0Z1YVRBb0JnTlZIU1VFSVRBZkJnZ3JCZ0VGQlFjREFnWUtLd1lCQkFHQ054UUMNCkFnWUhLd1lCQlFJREJEQXpCZ05WSFI4RUxEQXFNQ2lnSnFBa2hpSm9kSFJ3T2k4dlkzSnNMbUZ3Y0d4bExtTnYNCmJTOXdhMmx1YVhSallUSXVZM0pzTUIwR0ExVWREZ1FXQkJTRlIxOExjWkgxY1laNHlEajVyWXkwMmZNeTREQU8NCkJnTlZIUThCQWY4RUJBTUNCNEF3RUFZSktvWklodmRqWkFZcEJBTU1BVEl3Q2dZSUtvWkl6ajBFQXdJRFNRQXcNClJnSWhBSk9YTnBEWE43QjhHZFZIVE1WdmEzSForeXlCTDlMcm1hZTJkeFpUUUVoekFpRUF4b25CRjhnMTNQeXYNCkhCRFVSY0p0ZURUYVdQdnJyUW9HUi9KZXQzb3B5R1U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K\"}}"
  },
  {
    "path": "Tests/ContainerizationEXT4Tests/TestEXT4ExtendedAttributes.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//  swiftlint:disable force_try\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationEXT4\n\nstruct TestEXT4ExtendedAttribute {\n    @Test func compressName() {\n        struct TestCase {\n            let input: String\n            let expectedId: UInt8\n            let expectedStr: String\n            init(_ input: String, _ expectedId: UInt8, _ expectedStr: String) {\n                self.input = input\n                self.expectedId = expectedId\n                self.expectedStr = expectedStr\n            }\n        }\n        let tests: [TestCase] = [\n            .init(\"my.test.xattr\", 0, \"my.test.xattr\"),\n            .init(\"user.fubar\", 1, \"fubar\"),\n            .init(\"system.posix_acl_access.denied_su\", 2, \".denied_su\"),\n            .init(\"system.posix_acl_default_failed\", 3, \"_failed\"),\n            .init(\"trusted.user\", 4, \"user\"),\n            .init(\"trusted_user\", 0, \"trusted_user\"),\n            .init(\"security.auth\", 6, \"auth\"),\n            .init(\"system.admin\", 7, \"admin\"),\n            .init(\"system.richacl.denied\", 8, \".denied\"),\n        ]\n        for test in tests {\n            let ret = EXT4.ExtendedAttribute.compressName(test.input)\n            #expect(ret.0 == test.expectedId)\n            #expect(ret.1 == test.expectedStr)\n        }\n    }\n\n    @Test func encodeDecodeAttributes() {\n        let xattrs: [String: Data] = [\n            \"foo.bar\": Data([1, 2, 3]),\n            \"bar\": Data([0, 0, 0]),\n            \"system.richacl.bar\": Data([99, 1, 9, 1]),\n            \"foobar.user\": Data([71, 2, 45]),\n            \"test.xattr.cap\": Data([1, 32, 3]),\n            \"testing123\": Data([12, 24, 45]),\n            \"sys.admin\": Data([16, 23, 13]),\n            \"test.123\": Data([15, 26, 54]),\n            \"extendedattribute.test\": Data([15, 26, 54, 1, 2, 4, 6, 7, 7]),\n        ]\n        let blockSize = 4096\n        var state = EXT4.FileXattrsState(\n            inode: 1, inodeXattrCapacity: EXT4.InodeExtraSize, blockCapacity: UInt32(blockSize))\n        for (s, d) in xattrs {\n            let attribute = EXT4.ExtendedAttribute(name: s, value: [UInt8](d))\n            try! state.add(attribute)\n        }\n        var inlineAttrBuffer: [UInt8] = .init(repeating: 0, count: Int(EXT4.InodeExtraSize))\n        var blockAttrBuffer: [UInt8] = .init(repeating: 0, count: blockSize)\n        try! state.writeInlineAttributes(buffer: &inlineAttrBuffer)\n        try! state.writeBlockAttributes(buffer: &blockAttrBuffer)\n        let gotInlineXattrs = try! EXT4.EXT4Reader.readInlineExtendedAttributes(from: inlineAttrBuffer)\n        let gotBlockXattrs = try! EXT4.EXT4Reader.readBlockExtendedAttributes(from: blockAttrBuffer)\n\n        var gotXattrs: [String: Data] = [:]\n        for attr in gotBlockXattrs + gotInlineXattrs {\n            gotXattrs[attr.fullName] = Data(attr.value)\n        }\n        #expect(gotXattrs == xattrs)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationEXT4Tests/TestEXT4Format+Create.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//\n\nimport Foundation\nimport SystemPackage\nimport Testing\n\n@testable import ContainerizationEXT4\n\nstruct Ext4FormatCreateTests {\n    @Test func fileReplace() throws {\n        let fsPath = FilePath(\n            FileManager.default.temporaryDirectory\n                .appendingPathComponent(UUID().uuidString, isDirectory: false))\n        defer { try? FileManager.default.removeItem(at: fsPath.url) }\n\n        let formatter = try EXT4.Formatter(fsPath, minDiskSize: 32.kib())\n        defer { try? formatter.close() }\n        try formatter.create(path: FilePath(\"/file\"), mode: EXT4.Inode.Mode(.S_IFREG, 0o755), buf: nil)  // create a regular file\n        #expect(throws: Never.self) {\n            try formatter.create(path: FilePath(\"/file\"), mode: EXT4.Inode.Mode(.S_IFREG, 0o755), buf: nil)\n        }  // overwrite it with a regular file\n        #expect(throws: Error.self) { try formatter.create(path: FilePath(\"/file\"), mode: EXT4.Inode.Mode(.S_IFDIR, 0o700)) }  // overwrite it with a directory\n    }\n\n    @Test func dirReplace() throws {\n        let fsPath = FilePath(\n            FileManager.default.temporaryDirectory\n                .appendingPathComponent(UUID().uuidString, isDirectory: false))\n        defer { try? FileManager.default.removeItem(at: fsPath.url) }\n\n        let formatter = try EXT4.Formatter(fsPath, minDiskSize: 32.kib())\n        defer { try? formatter.close() }\n        try formatter.create(path: FilePath(\"/dir\"), mode: EXT4.Inode.Mode(.S_IFDIR, 0o700))  // create a directory\n        #expect(throws: Never.self) {\n            try formatter.create(path: FilePath(\"/dir\"), mode: EXT4.Inode.Mode(.S_IFDIR, 0o700))\n        }  // overwrite it with a directory\n        #expect(throws: Error.self) { try formatter.create(path: FilePath(\"/dir\"), mode: EXT4.Inode.Mode(.S_IFREG, 0o755)) }  // overwrite it with a file\n    }\n\n    @Test func fileParentFails() throws {\n        let fsPath = FilePath(\n            FileManager.default.temporaryDirectory\n                .appendingPathComponent(UUID().uuidString, isDirectory: false))\n        defer { try? FileManager.default.removeItem(at: fsPath.url) }\n\n        let formatter = try EXT4.Formatter(fsPath, minDiskSize: 32.kib())\n        defer { try? formatter.close() }\n        try formatter.create(path: FilePath(\"/file\"), mode: EXT4.Inode.Mode(.S_IFREG, 0o755), buf: nil)  // create a regular file\n        #expect(throws: Error.self) { try formatter.create(path: FilePath(\"/file/dir\"), mode: EXT4.Inode.Mode(.S_IFDIR, 0o700)) }  // create a subdir in a file?\n    }\n\n    @Test func createParentAutomatically() throws {\n        let fsPath = FilePath(\n            FileManager.default.temporaryDirectory\n                .appendingPathComponent(UUID().uuidString, isDirectory: false))\n        defer { try? FileManager.default.removeItem(at: fsPath.url) }\n\n        let formatter = try EXT4.Formatter(fsPath, minDiskSize: 32.kib())\n        defer { try? formatter.close() }\n        #expect(throws: Never.self) {\n            try formatter.create(path: FilePath(\"/parent/file\"), mode: EXT4.Inode.Mode(.S_IFREG, 0o755), buf: nil)\n        }  // should create /parent automatically\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationEXT4Tests/TestEXT4Format.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//  swiftlint: disable force_try shorthand_operator static_over_final_class\n\nimport Foundation\nimport SystemPackage\nimport Testing\n\n@testable import ContainerizationEXT4\n\nstruct Ext4FormatTests: ~Copyable {\n    let fsPath = FilePath(\n        FileManager.default.uniqueTemporaryDirectory()\n            .appendingPathComponent(\"ext4.img.delme.format\", isDirectory: false))\n\n    // This test creates a file named \"ext4.img.delme\"\n    // Since there are no tools yet in osx/swift to test the created filesystem,\n    // the tests below perform the same checks as the following manual commands\n    //\n    // From project root\n    // $> backpack run -it -v SwiftExt4/Tests/SwiftExt4Tests/:/test -w test ubuntu:latest\n    // $> ls -lrth ext4.img.delme # should be only 44K\n    // $> e2fsck ext4.img.delme # should return 0\n    // $> dumpe2fs ext4.img.delme # should print info and return 0\n    // $> debugfs ext4.img.delme # should open the fs\n    //   debugfs 1.46.5 (30-Dec-2021)\n    //   debugfs:  ls\n    //   2  (12) .    2  (12) ..    15  (12) x    11  (20) lost+found    12  (12) ase\n    //   16  (12) y    0  (4016)\n    //\n    //  # check directory\n    //\n    //   debugfs:  stat /test\n    //   Inode: 12   Type: directory    Mode:  01274   Flags: 0xc0000\n    //   Generation: 0    Version: 0x00000000:00000000\n    //   User:     0   Group:     0   Size: 4096\n    //   File ACL: 0\n    //   Links: 3   Blockcount: 1\n    //   Fragment:  Address: 0    Number: 0    Size: 0\n    //    ctime: 0x6614b59f:8cdf6a34 -- Tue Apr  9 03:27:27 2024\n    //    atime: 0x6614b59f:8cdf6a34 -- Tue Apr  9 03:27:27 2024\n    //    mtime: 0x6614b59f:8cdf6a34 -- Tue Apr  9 03:27:27 2024\n    //    crtime: 0x6614b59f:8cdf6a34 -- Tue Apr  9 03:27:27 2024\n    //   Size of extra inode fields: 24\n    //   EXTENTS:\n    //    (0):5\n    //\n    //  # check regular file\n    //\n    //   debugfs:  stat /test/foo/bar/x\n    //   Inode: 15   Type: regular    Mode:  01363   Flags: 0xc0000\n    //   Generation: 0    Version: 0x00000000:00000000\n    //   User:     0   Group:     0   Size: 4\n    //   File ACL: 0\n    //   Links: 2   Blockcount: 1\n    //   Fragment:  Address: 0    Number: 0    Size: 0\n    //    ctime: 0x6614b59f:8ce91ef8 -- Tue Apr  9 03:27:27 2024\n    //    atime: 0x6614b59f:8ce91ef8 -- Tue Apr  9 03:27:27 2024\n    //    mtime: 0x6614b59f:8ce91ef8 -- Tue Apr  9 03:27:27 2024\n    //    crtime: 0x6614b59f:8ce91ef8 -- Tue Apr  9 03:27:27 2024\n    //   Size of extra inode fields: 24\n    //   EXTENTS:\n    //    (0):2\n    //\n    //   # check symlink\n    //\n    //   debugfs:  stat /y\n    //   Inode: 16   Type: symlink    Mode:  01675   Flags: 0x0\n    //   Generation: 0    Version: 0x00000000:00000000\n    //   User:     0   Group:     0   Size: 19\n    //   File ACL: 0\n    //   Links: 1   Blockcount: 0\n    //   Fragment:  Address: 0    Number: 0    Size: 0\n    //    ctime: 0x6614b59f:8cf052fc -- Tue Apr  9 03:27:27 2024\n    //    atime: 0x6614b59f:8cf052fc -- Tue Apr  9 03:27:27 2024\n    //    mtime: 0x6614b59f:8cf052fc -- Tue Apr  9 03:27:27 2024\n    //    crtime: 0x6614b59f:8cf052fc -- Tue Apr  9 03:27:27 2024\n    //   Size of extra inode fields: 24\n    //   Fast link dest: \"test/foo\"\n    //\n    //   # check hard link\n    //\n    //   debugfs:  stat x\n    //   Inode: 15   Type: regular    Mode:  01363   Flags: 0xc0000\n    //   Generation: 0    Version: 0x00000000:00000000\n    //   User:     0   Group:     0   Size: 4\n    //   File ACL: 0\n    //   Links: 2   Blockcount: 1\n    //   Fragment:  Address: 0    Number: 0    Size: 0\n    //    ctime: 0x6614b59f:8ce91ef8 -- Tue Apr  9 03:27:27 2024\n    //    atime: 0x6614b59f:8ce91ef8 -- Tue Apr  9 03:27:27 2024\n    //    mtime: 0x6614b59f:8ce91ef8 -- Tue Apr  9 03:27:27 2024\n    //    crtime: 0x6614b59f:8ce91ef8 -- Tue Apr  9 03:27:27 2024\n    //   Size of extra inode fields: 24\n    //   EXTENTS:\n    //    (0):2\n    //\n    // Mount and check\n    //\n    // $> mkdir -p mntpnt\n    // $> mount -t ext4 ext4.img.delme mntpnt\n    // $> # explore file tree\n    init() throws {\n        let formatter = try EXT4.Formatter(fsPath, minDiskSize: 32.kib())\n        try formatter.create(path: FilePath(\"/test\"), mode: EXT4.Inode.Mode(.S_IFDIR, 0o700))\n        try formatter.create(path: FilePath(\"/test/foo\"), mode: EXT4.Inode.Mode(.S_IFDIR, 0o700))\n        try formatter.create(path: FilePath(\"/test/foo/bar\"), mode: EXT4.Inode.Mode(.S_IFDIR, 0o700))\n        let inputStream = InputStream(data: \"test\".data(using: .utf8)!)\n        inputStream.open()\n        try formatter.create(\n            path: FilePath(\"/test/foo/bar/x\"), mode: EXT4.Inode.Mode(.S_IFREG, 0o755),\n            buf: inputStream)  // create a regular file\n        inputStream.close()\n        try formatter.link(link: FilePath(\"/x\"), target: FilePath(\"/test/foo/bar/x\"))\n        try formatter.create(\n            path: FilePath(\"/y\"), link: FilePath(\"test/foo\"), mode: EXT4.Inode.Mode(.S_IFLNK, 0o700))  // create a symlink\n\n        try formatter.close()\n    }\n\n    deinit {\n        try? FileManager.default.removeItem(at: fsPath.url)\n    }\n\n    /// This test checks that the size of the FS at fsPath is the minimum possible\n    /// for its data + metadata. It should be 44 kib or 11 blocks, expanded to accommodate\n    /// data requiring > 32KiB of space\n    @Test func fileSize() throws {\n        let f = try FileHandle(forReadingFrom: fsPath.url)\n        let size = try f.seekToEnd()\n        #expect(size == 128.mib())\n    }\n\n    /// This test checks that the superblock was created correctly\n    @Test func superblock() throws {\n        let f = try EXT4.EXT4Reader(blockDevice: fsPath)\n        #expect(f.superBlock.blocksCountLow == 32768)\n        #expect(f.superBlock.freeBlocksCountLow == 32246)  // total - 512 inode blocks\n    }\n\n    /// This test checks that the group descriptor has been set correctly\n    @Test func groupDescriptors() throws {\n        let f = try EXT4.EXT4Reader(blockDevice: fsPath)\n        let gd = try f.getGroupDescriptor(0)\n        #expect(gd.blockBitmapLow == 551)  // move over by 512 blocks (for inodes)\n        #expect(gd.inodeBitmapLow == 552)  // move over by 512 blocks (for inodes)\n        #expect(gd.inodeTableLow == 39)\n        #expect(gd.freeBlocksCountLow == 32246)  // 512 block used by larger inode table per block group\n        #expect(gd.freeInodesCountLow == 8176)  // 512 times the inodes\n        #expect(gd.usedDirsCountLow == 5)\n    }\n\n    /// This test checks that the block bitmap has been set correctly\n    @Test func blockBitmap() throws {\n        let ext4 = try EXT4.EXT4Reader(blockDevice: fsPath)\n        let gd = try ext4.getGroupDescriptor(1)\n        let blockBitmapOffset = gd.blockBitmapLow\n        let f = try #require(FileHandle(forReadingFrom: fsPath))\n        try f.seek(toOffset: ext4.blockSize * blockBitmapOffset)\n        let bitmapSize = ext4.superBlock.blocksPerGroup / 8\n        #expect(bitmapSize == 4096)\n        let _ = try f.read(\n            upToCount: Int(ext4.superBlock.blocksCountLow - ext4.superBlock.freeBlocksCountLow - 1) / 8 + 1)\n    }\n\n    /// This test checks that the inode bitmap has been set correctly\n    @Test func inodeBitmap() throws {\n        let ext4 = try EXT4.EXT4Reader(blockDevice: fsPath)\n        let gd = try ext4.getGroupDescriptor(1)\n        let inodeBitmapOffset = gd.inodeBitmapLow\n        let f = try #require(FileHandle(forReadingFrom: fsPath))\n        try f.seek(toOffset: ext4.blockSize * inodeBitmapOffset)\n        let bitmapSize = ext4.superBlock.inodesPerGroup / 8\n        #expect(bitmapSize == 1024)\n    }\n\n    /// This test checks that the inode table has been set correctly\n    @Test func inodeTable() throws {\n        let ext4 = try EXT4.EXT4Reader(blockDevice: fsPath)\n        let gd = try ext4.getGroupDescriptor(0)\n        let inodeTableOffset = gd.inodeTableLow\n        let f = try #require(FileHandle(forReadingFrom: fsPath))\n        try f.seek(toOffset: ext4.blockSize * inodeTableOffset)\n        let inodeTableSize = ext4.superBlock.inodesPerGroup * UInt32(ext4.superBlock.inodeSize)\n        #expect(inodeTableSize == 2_097_152)\n        let inodeTableData = try #require(try f.read(upToCount: Int(inodeTableSize)))\n        let inodeAt: (Int) -> EXT4.Inode = { inodeNum in\n            var inodeBytes: [UInt8] = .init(repeating: 0, count: Int(ext4.superBlock.inodeSize))\n            let inodeStart = Int(ext4.superBlock.inodeSize) * (inodeNum - 1)\n            var j: Int = 0\n            for i in inodeStart..<inodeStart + Int(ext4.superBlock.inodeSize) {\n                inodeBytes[j] = inodeTableData[i]\n                j = j + 1\n            }\n            return inodeBytes.withUnsafeBytes { ptr in\n                ptr.loadLittleEndian(as: EXT4.Inode.self)\n            }\n        }\n        let root = inodeAt(2)\n        #expect(root.mode.isDir())\n        #expect(root.linksCount == 4)\n\n        let regFile = inodeAt(15)\n        #expect(regFile.mode.isReg())\n        #expect(regFile.sizeLow == 4)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationEXT4Tests/TestEXT4Reader+IO.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport SystemPackage\nimport Testing\n\n@testable import ContainerizationEXT4\n\n@Suite\nstruct EXT4PathIOTests {\n\n    // MARK: - Helpers\n\n    private func makeTempImageURL(name: String = UUID().uuidString) -> URL {\n        FileManager.default.temporaryDirectory.appendingPathComponent(\"ext4-\\(name).img\")\n    }\n\n    /// Build a fresh ext4 image, populate content, close, and return its URL.\n    /// Usage:\n    ///   let url = try buildFS { fmt in\n    ///       try createFile(fmt, \"/etc/hostname\", \"myhost\\n\")\n    ///       try createSymlink(fmt, \"/bin/sh\", \"/usr/bin/busybox\") // fast link\n    ///   }\n    private func buildFS(\n        minDiskSize: UInt64 = 4 * 1024 * 1024,  // 4 MiB is enough for these tests\n        blockSize: UInt32 = 4096,\n        populate: (EXT4.Formatter) throws -> Void\n    ) throws -> URL {\n        let url = makeTempImageURL()\n        let path = FilePath(url.path)\n\n        // 1) Format image\n        let formatter = try EXT4.Formatter(path, blockSize: blockSize, minDiskSize: minDiskSize)\n\n        // 2) Populate contents\n        try populate(formatter)\n\n        // 3) Finalize filesystem\n        try formatter.close()\n        return url\n    }\n\n    /// Convenience to create a directory (recursively).\n    private func createDir(_ fmt: EXT4.Formatter, _ path: String, mode: UInt16 = EXT4.Inode.Mode(.S_IFDIR, 0o755)) throws {\n        try fmt.create(path: FilePath(path), mode: mode)\n    }\n\n    /// Convenience to create a regular file with UTF-8 content.\n    private func createFile(_ fmt: EXT4.Formatter, _ path: String, _ contents: String, mode: UInt16 = EXT4.Inode.Mode(.S_IFREG, 0o644)) throws {\n        let data = Data(contents.utf8)\n        let stream = InputStream(data: data)\n        stream.open()\n        defer { stream.close() }\n        try fmt.create(path: FilePath(path), mode: mode, buf: stream)\n    }\n\n    /// Convenience to create a (fast or long) symlink. Pass absolute target.\n    private func createSymlink(_ fmt: EXT4.Formatter, _ linkPath: String, _ target: String, mode: UInt16 = EXT4.Inode.Mode(.S_IFLNK, 0o777)) throws {\n        try fmt.create(path: FilePath(linkPath), link: FilePath(target), mode: mode)\n    }\n\n    /// Open reader for a given image URL.\n    private func openReader(_ url: URL) throws -> EXT4.EXT4Reader {\n        try EXT4.EXT4Reader(blockDevice: FilePath(url.path))\n    }\n\n    // MARK: - Tests\n\n    @Test\n    func existsAndStatRootAndLostFound() throws {\n        let url = try buildFS { _ in /* nothing extra */ }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n        #expect(r.exists(FilePath(\"/\")))\n        let (inoNum, ino) = try r.stat(FilePath(\"/\"))\n        #expect(inoNum == 2)\n        #expect(ino.mode.isDir())\n\n        // lost+found is created by the formatter\n        let names = try r.listDirectory(FilePath(\"/\"))\n        #expect(names.contains(\"lost+found\"))\n    }\n\n    @Test\n    func createAndReadRegularFilesWithOffsetsAndEOF() throws {\n        let url = try buildFS { fmt in\n            try self.createDir(fmt, \"/etc\")\n            try self.createFile(fmt, \"/etc/hostname\", \"myhost\\n\")\n            try self.createDir(fmt, \"/usr/bin\")\n            try self.createFile(fmt, \"/usr/bin/hello\", \"Hello EXT4!\\n\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n\n        // exists/stat\n        #expect(r.exists(FilePath(\"/etc/hostname\")))\n        let (_, ino) = try r.stat(FilePath(\"/usr/bin/hello\"))\n        #expect(ino.mode.isReg())\n\n        // listDirectory excludes \".\" and \"..\" and is sorted\n        let usrChildren = try r.listDirectory(FilePath(\"/usr\"))\n        #expect(usrChildren == [\"bin\"])\n\n        // full read to EOF\n        let hello = try r.readFile(at: FilePath(\"/usr/bin/hello\"))\n        #expect(String(decoding: hello, as: UTF8.self) == \"Hello EXT4!\\n\")\n\n        // offset + count semantics\n        let data = try r.readFile(at: FilePath(\"/usr/bin/hello\"), offset: 6, count: 5)\n        #expect(String(decoding: data, as: UTF8.self) == \"EXT4!\")\n\n        // offset == size => empty; offset > size => empty\n        let hostname = try r.readFile(at: FilePath(\"/etc/hostname\"))\n        let size = hostname.count\n        #expect(try r.readFile(at: FilePath(\"/etc/hostname\"), offset: UInt64(size)).count == 0)\n        #expect(try r.readFile(at: FilePath(\"/etc/hostname\"), offset: UInt64(size + 100)).count == 0)\n    }\n\n    @Test\n    func readFileIntoBufferMatchesData() throws {\n        let url = try buildFS { fmt in\n            try self.createDir(fmt, \"/etc\")\n            try self.createFile(fmt, \"/etc/hostname\", \"myhost\\n\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let reader = try openReader(url)\n        let path = FilePath(\"/etc/hostname\")\n\n        let data = try reader.readFile(at: path, offset: 0, count: 4096)\n        var buffer = [UInt8](repeating: 0, count: data.count)\n\n        let wrote = try buffer.withUnsafeMutableBytes { ptr in\n            try reader.readFile(at: path, into: ptr, offset: 0)\n        }\n\n        #expect(wrote == data.count)\n        if wrote < buffer.count {\n            buffer.removeSubrange(wrote..<buffer.count)\n        }\n        #expect(Data(buffer) == data)\n    }\n\n    @Test\n    func listDirectoryOnFileThrowsNotAFile() throws {\n        let url = try buildFS { fmt in\n            try self.createDir(fmt, \"/etc\")\n            try self.createFile(fmt, \"/etc/hostname\", \"host\\n\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n        do {\n            _ = try r.listDirectory(FilePath(\"/etc/hostname\"))\n            Issue.record(\"Expected notADirectory error\")\n        } catch let error as EXT4.PathIOError {\n            guard case .notADirectory = error else {\n                Issue.record(\"Expected notADirectory, got \\(error)\")\n                return\n            }\n        } catch {\n            Issue.record(\"Expected EXT4.PathIOError, got \\(error)\")\n        }\n    }\n\n    @Test\n    func fastSymlinkResolutionAndRead() throws {\n        // Fast symlink: target stored in inode \"block\" area if len < 60 (Formatter behavior).\n        // Create /usr/bin/busybox and /bin/sh -> /usr/bin/busybox\n        let url = try buildFS { fmt in\n            try self.createDir(fmt, \"/usr/bin\")\n            try self.createFile(fmt, \"/usr/bin/busybox\", \"BB\\n\")\n            try self.createDir(fmt, \"/bin\")\n            try self.createSymlink(fmt, \"/bin/sh\", \"/usr/bin/busybox\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n\n        // Reading through the link should hit the target bytes.\n        let sh = try r.readFile(at: FilePath(\"/bin/sh\"))\n        #expect(String(decoding: sh, as: UTF8.self) == \"BB\\n\")\n\n        // stat(followSymlinks: false) should show symlink inode\n        let (_, lnkIno) = try r.stat(FilePath(\"/bin/sh\"), followSymlinks: false)\n        #expect(lnkIno.mode.isLink())\n\n        // stat(default) follows\n        let (_, tgtIno) = try r.stat(FilePath(\"/bin/sh\"))\n        #expect(tgtIno.mode.isReg())\n    }\n\n    @Test\n    func longSymlinkResolutionAndRead() throws {\n        // Long symlink: target > 60 bytes triggers extent-backed storage (Formatter behavior).\n        // Build a very long absolute path to exceed 60 chars.\n        let deepDir = \"/a/very/long/path/that/exceeds/sixty/bytes/for/symlink/target\"\n        #expect(deepDir.utf8.count > 60)\n\n        let url = try buildFS { fmt in\n            // Create deep directory structure and file\n            try self.createDir(fmt, deepDir)\n            try self.createFile(fmt, \"\\(deepDir)/payload.txt\", \"LONGLINK\\n\")\n            // Link at a short path -> long absolute target\n            try self.createSymlink(fmt, \"/ll\", \"\\(deepDir)/payload.txt\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n        let bytes = try r.readFile(at: FilePath(\"/ll\"))\n        #expect(String(decoding: bytes, as: UTF8.self) == \"LONGLINK\\n\")\n    }\n\n    @Test\n    func symlinkLoopDetection() throws {\n        let url = try buildFS { fmt in\n            // /a -> /b and /b -> /a\n            try self.createSymlink(fmt, \"/a\", \"/b\")\n            try self.createSymlink(fmt, \"/b\", \"/a\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n        do {\n            _ = try r.stat(FilePath(\"/a\"))\n            Issue.record(\"Expected symlinkLoop error\")\n        } catch let error as EXT4.PathIOError {\n            guard case .symlinkLoop = error else {\n                Issue.record(\"Expected symlinkLoop, got \\(error)\")\n                return\n            }\n        } catch {\n            Issue.record(\"Expected EXT4.PathIOError, got \\(error)\")\n        }\n    }\n\n    @Test\n    func complexSymlinkLoopDetection() throws {\n        let url = try buildFS { fmt in\n            // Create a longer chain that eventually loops: /a -> /b -> /c -> /d -> /b\n            try self.createSymlink(fmt, \"/a\", \"/b\")\n            try self.createSymlink(fmt, \"/b\", \"/c\")\n            try self.createSymlink(fmt, \"/c\", \"/d\")\n            try self.createSymlink(fmt, \"/d\", \"/b\")  // Loop back to /b\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n        do {\n            _ = try r.stat(FilePath(\"/a\"))\n            Issue.record(\"Expected symlinkLoop error\")\n        } catch let error as EXT4.PathIOError {\n            guard case .symlinkLoop = error else {\n                Issue.record(\"Expected symlinkLoop for complex loop, got \\(error)\")\n                return\n            }\n        } catch {\n            Issue.record(\"Expected EXT4.PathIOError, got \\(error)\")\n        }\n    }\n\n    @Test\n    func selfReferencingSymlink() throws {\n        let url = try buildFS { fmt in\n            // Self-referencing symlink: /self -> /self\n            try self.createSymlink(fmt, \"/self\", \"/self\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n        do {\n            _ = try r.stat(FilePath(\"/self\"))\n            Issue.record(\"Expected symlinkLoop error\")\n        } catch let error as EXT4.PathIOError {\n            guard case .symlinkLoop = error else {\n                Issue.record(\"Expected symlinkLoop for self-reference, got \\(error)\")\n                return\n            }\n        } catch {\n            Issue.record(\"Expected EXT4.PathIOError, got \\(error)\")\n        }\n    }\n\n    @Test\n    func longSymlinkChainWithoutLoop() throws {\n        let url = try buildFS { fmt in\n            // Create a long chain without loops (should succeed)\n            try self.createDir(fmt, \"/target\")\n            try self.createFile(fmt, \"/target/file.txt\", \"SUCCESS\\n\")\n\n            // Create chain: /link1 -> /link2 -> /link3 -> /link4 -> /target/file.txt\n            try self.createSymlink(fmt, \"/link4\", \"/target/file.txt\")\n            try self.createSymlink(fmt, \"/link3\", \"/link4\")\n            try self.createSymlink(fmt, \"/link2\", \"/link3\")\n            try self.createSymlink(fmt, \"/link1\", \"/link2\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n        // Should successfully resolve without hitting loop detection\n        let data = try r.readFile(at: FilePath(\"/link1\"))\n        #expect(String(decoding: data, as: UTF8.self) == \"SUCCESS\\n\")\n    }\n\n    @Test\n    func symlinkLoopThroughDirectory() throws {\n        let url = try buildFS { fmt in\n            // Create directory structure with symlink loop through paths\n            try self.createDir(fmt, \"/dir1\")\n            try self.createDir(fmt, \"/dir2\")\n            try self.createSymlink(fmt, \"/dir1/link\", \"/dir2/link\")\n            try self.createSymlink(fmt, \"/dir2/link\", \"/dir1/link\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n        do {\n            _ = try r.stat(FilePath(\"/dir1/link\"))\n            Issue.record(\"Expected symlinkLoop error\")\n        } catch let error as EXT4.PathIOError {\n            guard case .symlinkLoop = error else {\n                Issue.record(\"Expected symlinkLoop for directory loop, got \\(error)\")\n                return\n            }\n        } catch {\n            Issue.record(\"Expected EXT4.PathIOError, got \\(error)\")\n        }\n    }\n\n    @Test\n    func pathWalkWithDotAndDotDot() throws {\n        let url = try buildFS { fmt in\n            try self.createDir(fmt, \"/a/b\")\n            try self.createFile(fmt, \"/a/b/c.txt\", \"OK\\n\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n\n        // /a/./b/../b/c.txt should resolve to /a/b/c.txt\n        let p = FilePath(\"/a/./b/../b/c.txt\")\n        let data = try r.readFile(at: p)\n        #expect(String(decoding: data, as: UTF8.self) == \"OK\\n\")\n    }\n\n    @Test\n    func parentDirectoryTraversal() throws {\n        let url = try buildFS { fmt in\n            try self.createDir(fmt, \"/a/b/c/d\")\n            try self.createFile(fmt, \"/a/file1.txt\", \"A\\n\")\n            try self.createFile(fmt, \"/a/b/file2.txt\", \"B\\n\")\n            try self.createFile(fmt, \"/a/b/c/file3.txt\", \"C\\n\")\n            try self.createFile(fmt, \"/a/b/c/d/file4.txt\", \"D\\n\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n\n        // Test multiple levels of parent traversal\n        // /a/b/c/d/../../../../a/file1.txt should resolve to /a/file1.txt\n        let p1 = FilePath(\"/a/b/c/d/../../../../a/file1.txt\")\n        let data1 = try r.readFile(at: p1)\n        #expect(String(decoding: data1, as: UTF8.self) == \"A\\n\")\n\n        // /a/b/c/../file2.txt should resolve to /a/b/file2.txt\n        let p2 = FilePath(\"/a/b/c/../file2.txt\")\n        let data2 = try r.readFile(at: p2)\n        #expect(String(decoding: data2, as: UTF8.self) == \"B\\n\")\n\n        // /a/b/c/d/../file3.txt should resolve to /a/b/c/file3.txt\n        let p3 = FilePath(\"/a/b/c/d/../file3.txt\")\n        let data3 = try r.readFile(at: p3)\n        #expect(String(decoding: data3, as: UTF8.self) == \"C\\n\")\n    }\n\n    @Test\n    func parentDirectoryAtRoot() throws {\n        let url = try buildFS { fmt in\n            try self.createFile(fmt, \"/root.txt\", \"ROOT\\n\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n\n        // Test that \"..\" at root stays at root\n        // /../root.txt should resolve to /root.txt\n        let p1 = FilePath(\"/../root.txt\")\n        let data1 = try r.readFile(at: p1)\n        #expect(String(decoding: data1, as: UTF8.self) == \"ROOT\\n\")\n\n        // /../../root.txt should also resolve to /root.txt\n        let p2 = FilePath(\"/../../root.txt\")\n        let data2 = try r.readFile(at: p2)\n        #expect(String(decoding: data2, as: UTF8.self) == \"ROOT\\n\")\n    }\n\n    @Test\n    func complexParentWithSymlinks() throws {\n        let url = try buildFS { fmt in\n            try self.createDir(fmt, \"/real/path\")\n            try self.createFile(fmt, \"/real/path/target.txt\", \"TARGET\\n\")\n            try self.createFile(fmt, \"/real/other.txt\", \"OTHER\\n\")\n            try self.createDir(fmt, \"/links\")\n            // Create symlink: /links/link -> /real/path\n            try self.createSymlink(fmt, \"/links/link\", \"/real/path\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n\n        // Test parent directory traversal through symlinks\n        // When we follow /links/link (which points to /real/path), we're now at /real/path\n        // Then \"..\" takes us to /real, and \"other.txt\" gives us /real/other.txt\n        let p = FilePath(\"/links/link/../other.txt\")\n        let data = try r.readFile(at: p)\n        #expect(String(decoding: data, as: UTF8.self) == \"OTHER\\n\")\n\n        // Also test direct access through the symlink\n        let p2 = FilePath(\"/links/link/target.txt\")\n        let data2 = try r.readFile(at: p2)\n        #expect(String(decoding: data2, as: UTF8.self) == \"TARGET\\n\")\n    }\n\n    @Test\n    func relativeSymlinkWithParentTraversal() throws {\n        let url = try buildFS { fmt in\n            try self.createDir(fmt, \"/a/b\")\n            try self.createFile(fmt, \"/a/target.txt\", \"REL_TARGET\\n\")\n            // Create relative symlink: /a/b/link -> ../target.txt\n            try self.createSymlink(fmt, \"/a/b/link\", \"../target.txt\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n\n        // The relative symlink should properly resolve through parent\n        let data = try r.readFile(at: FilePath(\"/a/b/link\"))\n        #expect(String(decoding: data, as: UTF8.self) == \"REL_TARGET\\n\")\n    }\n\n    @Test\n    func readOnSymlinkWithFollowFalseThrows() throws {\n        let url = try buildFS { fmt in\n            try self.createFile(fmt, \"/tgt\", \"X\\n\")\n            try self.createSymlink(fmt, \"/lnk\", \"/tgt\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n        do {\n            _ = try r.readFile(at: FilePath(\"/lnk\"), followSymlinks: false)\n            Issue.record(\"Expected notAFile error\")\n        } catch let error as EXT4.PathIOError {\n            guard case .notAFile = error else {\n                Issue.record(\"Expected notAFile for symlink read without following, got \\(error)\")\n                return\n            }\n        } catch {\n            Issue.record(\"Expected EXT4.PathIOError, got \\(error)\")\n        }\n    }\n\n    @Test\n    func nonExistentPathExistsAndReadErrors() throws {\n        let url = try buildFS { _ in }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n        #expect(!r.exists(FilePath(\"/nope\")))\n        do {\n            _ = try r.stat(FilePath(\"/nope\"))\n            Issue.record(\"Expected notFound error\")\n        } catch let error as EXT4.PathIOError {\n            guard case .notFound = error else {\n                Issue.record(\"Expected notFound, got \\(error)\")\n                return\n            }\n        } catch {\n            Issue.record(\"Expected EXT4.PathIOError, got \\(error)\")\n        }\n        #expect(throws: (any Error).self) {\n            try r.readFile(at: FilePath(\"/nope\"))\n        }\n    }\n\n    @Test\n    func boundsCheckingForInvalidExtents() throws {\n        // This test verifies that the reader properly validates extent addresses\n        // Note: We can't easily create an image with invalid extents using the Formatter,\n        // so this test documents the expected behavior rather than testing it directly.\n        // The bounds checking is tested implicitly by all other tests that read files.\n\n        let url = try buildFS { fmt in\n            try self.createFile(fmt, \"/test.txt\", \"Valid file\\n\")\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n\n        // Reading a valid file should work without bounds errors\n        let data = try r.readFile(at: FilePath(\"/test.txt\"))\n        #expect(String(decoding: data, as: UTF8.self) == \"Valid file\\n\")\n\n        // The bounds checking happens internally when reading extents\n        // If an extent pointed outside device bounds, it would throw an error\n    }\n\n    @Test\n    func partialReadRecovery() throws {\n        // Test that partial reads return successfully read data\n        // even if later parts fail\n\n        let url = try buildFS(minDiskSize: 8 * 1024 * 1024) { fmt in\n            // Create a moderately sized file\n            let content = String(repeating: \"A\", count: 100_000)\n            try self.createFile(fmt, \"/partial.txt\", content)\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n\n        // Read the full file to ensure it works\n        let fullData = try r.readFile(at: FilePath(\"/partial.txt\"))\n        #expect(fullData.count == 100_000)\n\n        // Read with offset and count\n        let partialData = try r.readFile(at: FilePath(\"/partial.txt\"), offset: 1000, count: 5000)\n        #expect(partialData.count == 5000)\n\n        // Verify content is correct\n        let expectedContent = String(repeating: \"A\", count: 5000)\n        #expect(String(decoding: partialData, as: UTF8.self) == expectedContent)\n    }\n\n    @Test\n    func largeFileReadAcrossBlocks() throws {\n        // Keep this modest to avoid slow CI while still crossing multiple blocks.\n        let bigSize = 2 * 1024 * 1024 + 123  // ~2 MiB + tail\n        let url = try buildFS(minDiskSize: 16 * 1024 * 1024) { fmt in\n            try self.createDir(fmt, \"/big\")\n            // Generate deterministic content without holding huge Data in memory at once\n            // by assembling in chunks.\n            let chunk = Data(\"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\\n\".utf8)\n            var buf = Data(capacity: bigSize)\n            while buf.count + chunk.count <= bigSize {\n                buf.append(chunk)\n            }\n            if buf.count < bigSize {\n                buf.append(contentsOf: [UInt8](repeating: 0x5A, count: bigSize - buf.count))  // 'Z'\n            }\n            let stream = InputStream(data: buf)\n            stream.open()\n            defer { stream.close() }\n            try fmt.create(path: FilePath(\"/big/file.bin\"), mode: EXT4.Inode.Mode(.S_IFREG, 0o644), buf: stream)\n        }\n        defer { try? FileManager.default.removeItem(at: url) }\n\n        let r = try openReader(url)\n\n        // Sample reads near the end and across likely block boundaries.\n        let tail = try r.readFile(at: FilePath(\"/big/file.bin\"), offset: UInt64(bigSize - 64), count: 64)\n        #expect(tail.count == 64)\n\n        let middle = try r.readFile(at: FilePath(\"/big/file.bin\"), offset: 64, count: 128)\n        #expect(middle.count == 128)\n\n        // Read to EOF without count\n        let all = try r.readFile(at: FilePath(\"/big/file.bin\"))\n        #expect(all.count == bigSize)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationEXT4Tests/TestEXT4Unpacker.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport SystemPackage\nimport Testing\n\n@testable import ContainerizationEXT4\n\nstruct Ext4UnpackerTests {\n    // alpine image\n    let indexSHA: String = \"ad59e9f71edceca7b1ac7c642410858489b743c97233b0a26a5e2098b1443762\"\n    let fsPath = FilePath(\n        FileManager.default.uniqueTemporaryDirectory()\n            .appendingPathComponent(\"ext4.unpacked.oci.img.delme\", isDirectory: false))\n\n    final class MockEXT4Unpacker {\n        static func Unpack(index: String, fsPath: FilePath) throws {\n            let fs = try EXT4.Formatter(fsPath)\n            let bundle = Bundle.module\n            guard let indexPath = bundle.url(forResource: index, withExtension: nil) else {\n                throw NSError(domain: \"indexPath not found\", code: 1)\n            }\n            let indexData = try Data(contentsOf: indexPath)\n            guard let indexDict = try JSONSerialization.jsonObject(with: indexData, options: []) as? [String: Any]\n            else {\n                throw NSError(domain: \"indexDict could not be loaded as json\", code: 1)\n            }\n            guard let manifests = indexDict[\"manifests\"] as? [[String: Any]] else {\n                throw NSError(domain: \"manifests field in index not found\", code: 1)\n            }\n            guard let digest = manifests[0][\"digest\"] as? String else {\n                throw NSError(domain: \"digest field not found in index\", code: 1)\n            }\n            guard\n                let manifestPath = bundle.url(\n                    forResource: String(digest.dropFirst(\"sha256:\".count)), withExtension: nil)\n            else {\n                throw NSError(domain: \"manifestPath not found\", code: 1)\n            }\n            let manifestData = try Data(contentsOf: manifestPath)\n            guard let manifestDict = try JSONSerialization.jsonObject(with: manifestData, options: []) as? [String: Any]\n            else {\n                throw NSError(domain: \"manifestDict could not be loaded as json\", code: 1)\n            }\n            guard let layers = manifestDict[\"layers\"] as? [[String: Any]] else {\n                throw NSError(domain: \"layers field in manifests not found\", code: 1)\n            }\n            for layer in layers {\n                guard let layerDigestWithSHA = layer[\"digest\"] as? String else {\n                    throw NSError(domain: \"digest field not found in layer\", code: 1)\n                }\n                let layerDigest = String(layerDigestWithSHA.dropFirst(\"sha256:\".count))\n                guard let layerPath = bundle.url(forResource: layerDigest, withExtension: nil) else {\n                    throw NSError(domain: \"layer \\(layerDigest) not found\", code: 1)\n                }\n                try fs.unpack(source: layerPath)\n            }\n            try fs.close()\n        }\n    }\n\n    @Test func eXT4Unpacker() throws {\n        try MockEXT4Unpacker.Unpack(index: self.indexSHA, fsPath: self.fsPath)\n        let ext4 = try EXT4.EXT4Reader(blockDevice: self.fsPath)\n        let children = try ext4.children(of: EXT4.RootInode)\n        #expect(\n            Set(children.map { $0.0 })\n                == Set([\n                    \".\",\n                    \"..\",\n                    \"media\",\n                    \"var\",\n                    \"opt\",\n                    \"lost+found\",\n                    \"tmp\",\n                    \"mnt\",\n                    \"sys\",\n                    \"usr\",\n                    \"srv\",\n                    \"root\",\n                    \"etc\",\n                    \"dev\",\n                    \"proc\",\n                    \"run\",\n                    \"home\",\n                    \"bin\",\n                    \"lib\",\n                    \"sbin\",\n                ]))\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationEXT4Tests/TestFormatterUnpack.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n// swiftlint:disable force_try static_over_final_class\n\n#if os(macOS)\nimport ContainerizationArchive\nimport Foundation\nimport Testing\nimport SystemPackage\n\n@testable import ContainerizationEXT4\n\nstruct Tar2EXT4Test: ~Copyable {\n    let fsPath = FilePath(\n        FileManager.default.uniqueTemporaryDirectory()\n            .appendingPathComponent(\"ext4.tar.img.delme\", isDirectory: false))\n\n    let xattrs: [String: Data] = [\n        \"foo.bar\": Data([1, 2, 3]),\n        \"bar\": Data([0, 0, 0]),\n        \"system.richacl.bar\": Data([99, 1, 9, 1]),\n        \"foobar.user\": Data([71, 2, 45]),\n        \"test.xattr.cap\": Data([1, 32, 3]),\n        \"testing123\": Data([12, 24, 45]),\n        \"sys.admin\": Data([16, 23, 13]),\n        \"test.123\": Data([15, 26, 54]),\n        \"extendedattribute.test\": Data([15, 26, 54, 1, 2, 4, 6, 7, 7]),\n    ]\n\n    init() throws {\n        // create layer1\n        let layer1Path = FileManager.default.uniqueTemporaryDirectory()\n            .appendingPathComponent(\"layer1.tar.gz\", isDirectory: false)\n        let layer1Archiver = try ArchiveWriter(\n            configuration: ArchiveWriterConfiguration(format: .paxRestricted, filter: .gzip))\n        try layer1Archiver.open(file: layer1Path)\n        // create 2 directories and fill them with files\n        try layer1Archiver.writeEntry(entry: WriteEntry.dir(path: \"/dir1\", permissions: 0o755), data: nil)\n        try layer1Archiver.writeEntry(entry: WriteEntry.file(path: \"/dir1/file1\", permissions: 0o644), data: nil)\n        try layer1Archiver.writeEntry(entry: WriteEntry.dir(path: \"/dir2\", permissions: 0o755), data: nil)\n        try layer1Archiver.writeEntry(entry: WriteEntry.file(path: \"/dir2/file1\", permissions: 0o644), data: nil)\n        try layer1Archiver.finishEncoding()\n\n        // create layer2\n        let layer2Path = FileManager.default.uniqueTemporaryDirectory()\n            .appendingPathComponent(\"layer2.tar.gz\", isDirectory: false)\n        let layer2Archiver = try ArchiveWriter(\n            configuration: ArchiveWriterConfiguration(format: .paxRestricted, filter: .gzip))\n        try layer2Archiver.open(file: layer2Path)\n        // create 3 directories and fill them with files and whiteouts\n        try layer2Archiver.writeEntry(entry: WriteEntry.dir(path: \"/dir1\", permissions: 0o755), data: nil)\n        try layer2Archiver.writeEntry(\n            entry: WriteEntry.file(path: \"/dir1/.wh.file1\", permissions: 0o644), data: nil)\n        try layer2Archiver.writeEntry(entry: WriteEntry.dir(path: \"/dir2\", permissions: 0o755), data: nil)\n        try layer2Archiver.writeEntry(\n            entry: WriteEntry.file(path: \"/dir2/.wh..wh..opq\", permissions: 0o644), data: nil)\n        try layer2Archiver.writeEntry(entry: WriteEntry.dir(path: \"/dir3\", permissions: 0o755), data: nil)\n        try layer2Archiver.writeEntry(\n            entry: WriteEntry.file(path: \"/dir3/file1\", permissions: 0o644, xattrs: xattrs), data: nil)\n        try layer2Archiver.writeEntry(entry: WriteEntry.dir(path: \"/dir4\", permissions: 0o755), data: nil)\n        try layer2Archiver.writeEntry(\n            entry: WriteEntry.file(path: \"/dir4/special_ÆÂ©\", permissions: 0o644), data: nil)\n        try layer2Archiver.writeEntry(\n            entry: WriteEntry.link(path: \"/dir4/specialcharacters\", permissions: 0o644, target: \"special_ÆÂ©\"),\n            data: nil)\n\n        // a new layer overwriting over an existing layer\n        try layer2Archiver.writeEntry(entry: WriteEntry.file(path: \"/dir2/file1\", permissions: 0o644), data: nil)\n        try layer2Archiver.finishEncoding()\n\n        let unpacker = try EXT4.Formatter(fsPath)\n        try unpacker.unpack(source: layer1Path)\n        try unpacker.unpack(source: layer2Path)\n        try unpacker.close()\n    }\n\n    deinit {\n        try? FileManager.default.removeItem(at: fsPath.url)\n    }\n\n    @Test func testUnpackBasic() throws {\n        let ext4 = try EXT4.EXT4Reader(blockDevice: fsPath)\n        // just a directory\n        let dir1Inode = try ext4.getInode(number: 12)\n        #expect(dir1Inode.mode.isDir())\n        // white out file /dir1/file1\n        let dir1File1Inode = try ext4.getInode(number: 13)\n        #expect(dir1File1Inode.dtime != 0)\n        #expect(dir1File1Inode.linksCount == 0)  // deleted\n        // white out dir /dir2\n        let dir2Inode = try ext4.getInode(number: 14)\n        #expect(dir2Inode.dtime == 0)\n        #expect(dir2Inode.linksCount == 2)  // children deleted\n        // new dir /dir3\n        let dir3Inode = try ext4.getInode(number: 16)\n        #expect(dir3Inode.mode.isDir())\n        #expect(dir3Inode.linksCount == 2)\n        // new file /dir3/file1\n        let dir3File1Inode = try ext4.getInode(number: 17)\n        #expect(dir3File1Inode.mode.isReg())\n        #expect(dir3File1Inode.linksCount == 1)\n        #expect(try ext4.getXattrsForInode(inode: dir3File1Inode) == xattrs)\n        // overwritten dir /dir2\n        let dir2OverwriteInode = try ext4.getInode(number: 18)\n        #expect(dir2OverwriteInode.mode.isDir())\n        #expect(dir2OverwriteInode.linksCount == 2)\n        // /dir4/special_ÆÂ©\n        let dir2File1OverwriteInode = try ext4.getInode(number: 19)\n        #expect(dir2File1OverwriteInode.mode.isReg())\n        #expect(dir2File1OverwriteInode.linksCount == 1)\n\n        let specialFileInode = try ext4.getInode(number: 20)\n        let bytes = Data(Mirror(reflecting: specialFileInode.block).children.compactMap { $0.value as? UInt8 })\n        let specialFileTarget = try #require(FilePath(bytes), \"Could not parse special file path\")\n        #expect(specialFileTarget.description.hasPrefix(\"special_ÆÂ©\"))\n    }\n}\n\nextension ContainerizationArchive.WriteEntry {\n    static func dir(path: String, permissions: UInt16) -> WriteEntry {\n        let entry = WriteEntry()\n        entry.path = path\n        entry.fileType = .directory\n        entry.permissions = permissions\n        return entry\n    }\n\n    static func file(path: String, permissions: UInt16, size: Int64? = nil, xattrs: [String: Data]? = nil) -> WriteEntry {\n        let entry = WriteEntry()\n        entry.path = path\n        entry.fileType = .regular\n        entry.permissions = permissions\n        entry.size = size\n        if let xattrs {\n            entry.xattrs = xattrs\n        }\n        return entry\n    }\n\n    static func link(path: String, permissions: UInt16, target: String) -> WriteEntry {\n        let entry = WriteEntry()\n        entry.path = path\n        entry.fileType = .symbolicLink\n        entry.symlinkTarget = target\n        return entry\n    }\n}\n\nextension EXT4.EXT4Reader {\n    fileprivate func getXattrsForInode(inode: EXT4.Inode) throws -> [String: Data] {\n        var attributes: [EXT4.ExtendedAttribute] = []\n        let buffer: [UInt8] = EXT4.tupleToArray(inode.inlineXattrs)\n        try attributes.append(contentsOf: Self.readInlineExtendedAttributes(from: buffer))\n        let block = inode.xattrBlockLow\n        try self.seek(block: block)\n        let buf = try self.handle.read(upToCount: Int(self.blockSize))!\n        try attributes.append(contentsOf: Self.readBlockExtendedAttributes(from: [UInt8](buf)))\n        var xattrs: [String: Data] = [:]\n        for attribute in attributes {\n            guard attribute.fullName != \"system.data\" else {\n                continue\n            }\n            xattrs[attribute.fullName] = Data(attribute.value)\n        }\n        return xattrs\n    }\n}\n#endif\n"
  },
  {
    "path": "Tests/ContainerizationExtrasTests/AsyncMutexTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationExtras\n\nfinal class AsyncMutexTests {\n    @Test\n    func testBasicModification() async throws {\n        let mutex = AsyncMutex(0)\n\n        let result = await mutex.withLock { value in\n            value += 1\n            return value\n        }\n\n        #expect(result == 1)\n    }\n\n    @Test\n    func testMultipleModifications() async throws {\n        let mutex = AsyncMutex(0)\n\n        await mutex.withLock { value in\n            value += 5\n        }\n\n        let result = await mutex.withLock { value in\n            value += 10\n            return value\n        }\n\n        #expect(result == 15)\n    }\n\n    @Test\n    func testConcurrentAccess() async throws {\n        let mutex = AsyncMutex(0)\n        let iterations = 100\n\n        await withTaskGroup(of: Void.self) { group in\n            for _ in 0..<iterations {\n                group.addTask {\n                    await mutex.withLock { value in\n                        value += 1\n                    }\n                }\n            }\n        }\n\n        let finalValue = await mutex.withLock { value in\n            value\n        }\n\n        #expect(finalValue == iterations)\n    }\n\n    @Test\n    func testAsyncOperationsUnderLock() async throws {\n        let mutex = AsyncMutex([Int]())\n\n        await mutex.withLock { value in\n            try? await Task.sleep(for: .milliseconds(10))\n            value.append(1)\n        }\n\n        await mutex.withLock { value in\n            try? await Task.sleep(for: .milliseconds(10))\n            value.append(2)\n        }\n\n        let result = await mutex.withLock { value in\n            value\n        }\n\n        #expect(result == [1, 2])\n    }\n\n    @Test\n    func testThrowingClosure() async throws {\n        let mutex = AsyncMutex(0)\n\n        await #expect(throws: POSIXError.self) {\n            try await mutex.withLock { value in\n                value += 1\n                throw POSIXError(.ENOENT)\n            }\n        }\n\n        // Value should still be modified even though closure threw\n        let result = await mutex.withLock { value in\n            value\n        }\n\n        #expect(result == 1)\n    }\n\n    @Test\n    func testComplexDataStructure() async throws {\n        struct Counter: Sendable {\n            var count: Int\n            var label: String\n        }\n\n        let mutex = AsyncMutex(Counter(count: 0, label: \"test\"))\n\n        await mutex.withLock { value in\n            value.count += 10\n            value.label = \"modified\"\n        }\n\n        await mutex.withLock { value in\n            #expect(value.count == 10)\n            #expect(value.label == \"modified\")\n        }\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationExtrasTests/ProxyUtilsTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationExtras\n\nstruct ProxyUtilsTests {\n\n    @Test(\"HTTP proxy resolution\")\n    func testHttpProxy() {\n        let env = [\"http_proxy\": \"http://proxy.local:8080\"]\n        let proxy = ProxyUtils.proxyFromEnvironment(scheme: \"http\", host: \"example.com\", env: env)\n        #expect(proxy?.absoluteString == \"http://proxy.local:8080\")\n    }\n\n    @Test(\"HTTP proxy miss\")\n    func testHttpProxyMiss() {\n        let env = [\"http_proxy\": \"http://proxy.local:8080\"]\n        let proxy = ProxyUtils.proxyFromEnvironment(scheme: \"https\", host: \"secure.com\", env: env)\n        #expect(proxy?.absoluteString == nil)\n    }\n\n    @Test(\"HTTPS proxy resolution\")\n    func testHttpsProxy() {\n        let env = [\"https_proxy\": \"https://secureproxy.local:8443\"]\n        let proxy = ProxyUtils.proxyFromEnvironment(scheme: \"https\", host: \"secure.com\", env: env)\n        #expect(proxy?.absoluteString == \"https://secureproxy.local:8443\")\n    }\n\n    @Test(\"HTTPS proxy miss\")\n    func testHttpsProxyMiss() {\n        let env = [\"https_proxy\": \"https://secureproxy.local:8443\"]\n        let proxy = ProxyUtils.proxyFromEnvironment(scheme: \"http\", host: \"example.com\", env: env)\n        #expect(proxy?.absoluteString == nil)\n    }\n\n    @Test(\"NO_PROXY exact match\")\n    func testNoProxyExactMatch() {\n        let env = [\n            \"http_proxy\": \"http://proxy.local:8080\",\n            \"NO_PROXY\": \"example.com\",\n        ]\n        let proxy = ProxyUtils.proxyFromEnvironment(scheme: \"http\", host: \"example.com\", env: env)\n        #expect(proxy == nil)\n    }\n\n    @Test(\"Uppercase HTTP_PROXY is respected\")\n    func testUppercaseHttpProxy() {\n        let env = [\"HTTP_PROXY\": \"http://upper.local:8081\"]\n        let proxy = ProxyUtils.proxyFromEnvironment(scheme: \"http\", host: \"upper.com\", env: env)\n        #expect(proxy?.absoluteString == \"http://upper.local:8081\")\n    }\n\n    @Test(\"Lowercase no_proxy is respected\")\n    func testLowercaseNoProxy() {\n        let env = [\n            \"http_proxy\": \"http://proxy.local:8080\",\n            \"no_proxy\": \"lower.com\",\n        ]\n        let proxy = ProxyUtils.proxyFromEnvironment(scheme: \"http\", host: \"lower.com\", env: env)\n        #expect(proxy == nil)\n    }\n\n    @Test(\"Uppercase HTTP_PROXY overrides lowercase http_proxy\")\n    func testUppercaseOverridesLowercaseHttp() {\n        let env = [\n            \"http_proxy\": \"http://lower.local:8080\",\n            \"HTTP_PROXY\": \"http://upper.local:8081\",\n        ]\n        let proxy = ProxyUtils.proxyFromEnvironment(scheme: \"http\", host: \"example.com\", env: env)\n        #expect(proxy?.absoluteString == \"http://upper.local:8081\")\n    }\n\n    @Test(\"Uppercase HTTPS_PROXY overrides lowercase https_proxy\")\n    func testUppercaseOverridesLowercaseHttps() {\n        let env = [\n            \"https_proxy\": \"https://lower.local:8443\",\n            \"HTTPS_PROXY\": \"https://upper.local:8444\",\n        ]\n        let proxy = ProxyUtils.proxyFromEnvironment(scheme: \"https\", host: \"secure.com\", env: env)\n        #expect(proxy?.absoluteString == \"https://upper.local:8444\")\n    }\n\n    @Test(\"Uppercase NO_PROXY overrides lowercase no_proxy\")\n    func testUppercaseOverridesLowercaseNoProxy() {\n        let env = [\n            \"http_proxy\": \"http://proxy.local:8080\",\n            \"no_proxy\": \"foo.com\",\n            \"NO_PROXY\": \"bar.com\",\n        ]\n        let proxyFoo = ProxyUtils.proxyFromEnvironment(scheme: \"http\", host: \"foo.com\", env: env)\n        let proxyBar = ProxyUtils.proxyFromEnvironment(scheme: \"https\", host: \"bar.com\", env: env)\n\n        #expect(proxyFoo != nil)\n        #expect(proxyBar == nil)\n    }\n\n    @Test(\"NO_PROXY with wildcard\")\n    func testComplexNoProxy() {\n        let env = [\n            \"HTTP_PROXY\": \"http://proxy.example.com\",\n            \"NO_PROXY\": \"localhost,127.0.0.1,*.bar.com\",\n        ]\n        let proxy = ProxyUtils.proxyFromEnvironment(scheme: \"http\", host: \"foo.bar.com\", env: env)\n        #expect(proxy == nil)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationExtrasTests/TestCIDR.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationExtras\n\nstruct TestCIDR {\n\n    // MARK: - Normalization Tests\n\n    struct Normalization {\n        let input: String\n        let expectedNetwork: String\n        let expectedLength: UInt8\n    }\n\n    @Test(arguments: [\n        Normalization(\n            input: \"192.168.1.100/24\",\n            expectedNetwork: \"192.168.1.0\",\n            expectedLength: 24\n        ),\n        Normalization(\n            input: \"10.1.2.3/16\",\n            expectedNetwork: \"10.1.0.0\",\n            expectedLength: 16\n        ),\n        Normalization(\n            input: \"2001:db8::1234/64\",\n            expectedNetwork: \"2001:db8::\",\n            expectedLength: 64\n        ),\n        Normalization(\n            input: \"172.16.0.1/12\",\n            expectedNetwork: \"172.16.0.0\",\n            expectedLength: 12\n        ),\n    ])\n    func testNormalization(testCase: Normalization) throws {\n        let cidr = try CIDR(testCase.input)\n        #expect(cidr.lower.description == testCase.expectedNetwork)\n        #expect(cidr.prefix.length == testCase.expectedLength)\n    }\n\n    struct ParsePreservation {\n        let input: String\n        let expectedIP: String\n        let expectedLength: UInt8\n    }\n\n    // MARK: - Bounds Tests\n\n    struct Bounds {\n        let cidr: String\n        let lower: String\n        let upper: String\n    }\n\n    @Test(arguments: [\n        Bounds(\n            cidr: \"192.168.1.0/24\",\n            lower: \"192.168.1.0\",\n            upper: \"192.168.1.255\"\n        ),\n        Bounds(\n            cidr: \"10.0.0.0/8\",\n            lower: \"10.0.0.0\",\n            upper: \"10.255.255.255\"\n        ),\n        Bounds(\n            cidr: \"2001:db8::/64\",\n            lower: \"2001:db8::\",\n            upper: \"2001:db8::ffff:ffff:ffff:ffff\"\n        ),\n        Bounds(\n            cidr: \"192.168.1.0/32\",\n            lower: \"192.168.1.0\",\n            upper: \"192.168.1.0\"\n        ),\n    ])\n    func testBounds(testCase: Bounds) throws {\n        let block = try CIDR(testCase.cidr)\n        #expect(block.lower.description == testCase.lower)\n        #expect(block.upper.description == testCase.upper)\n    }\n\n    // MARK: - Containment Tests\n\n    struct IPContainment {\n        let cidr: String\n        let ip: String\n        let shouldContain: Bool\n    }\n\n    @Test(arguments: [\n        IPContainment(\n            cidr: \"192.168.1.0/24\",\n            ip: \"192.168.1.100\",\n            shouldContain: true\n        ),\n        IPContainment(\n            cidr: \"192.168.1.0/24\",\n            ip: \"192.168.2.1\",\n            shouldContain: false\n        ),\n        IPContainment(\n            cidr: \"10.0.0.0/8\",\n            ip: \"10.255.255.255\",\n            shouldContain: true\n        ),\n        IPContainment(\n            cidr: \"10.0.0.0/8\",\n            ip: \"11.0.0.1\",\n            shouldContain: false\n        ),\n        IPContainment(\n            cidr: \"2001:db8::/32\",\n            ip: \"2001:db8::1\",\n            shouldContain: true\n        ),\n    ])\n    func testContainsIP(testCase: IPContainment) throws {\n        let block = try CIDR(testCase.cidr)\n        let address = try IPAddress(testCase.ip)\n        #expect(block.contains(address) == testCase.shouldContain)\n    }\n\n    @Test func testDoesNotContainDifferentIPv6Zone() throws {\n        let cidr = try CIDR(\"fe80::1/64\")\n        let ip = try IPv6Address(\"fe80::2%eth1\")\n        #expect(!cidr.contains(.v6(ip)))\n    }\n\n    // MARK: - Range Constructor\n\n    @Test func testRangeConstructorFindsSmallestBlock() throws {\n        let lower = try IPAddress(\"192.168.1.0\")\n        let upper = try IPAddress(\"192.168.1.255\")\n        let cidr = try CIDR(lower: lower, upper: upper)\n        #expect(cidr.prefix.length == 24)\n        #expect(cidr.address.description == \"192.168.1.0\")\n    }\n\n    @Test func testRangeConstructorSingleIPv4() throws {\n        let ip = try IPAddress(\"192.168.1.100\")\n        let cidr = try CIDR(lower: ip, upper: ip)\n        #expect(cidr.prefix.length == 32)\n        #expect(cidr.address.description == \"192.168.1.100\")\n    }\n\n    @Test func testRangeConstructorIPv6() throws {\n        let lower = try IPAddress(\"2001:db8::\")\n        let upper = try IPAddress(\"2001:db8::ffff:ffff:ffff:ffff\")\n        let cidr = try CIDR(lower: lower, upper: upper)\n        #expect(cidr.prefix.length == 64)\n        #expect(cidr.address.description == \"2001:db8::\")\n    }\n\n    @Test func testRangeConstructorSingleIPv6() throws {\n        let ip = try IPAddress(\"2001:db8::1\")\n        let cidr = try CIDR(lower: ip, upper: ip)\n        #expect(cidr.prefix.length == 128)\n        #expect(cidr.address.description == \"2001:db8::1\")\n    }\n\n    @Test func testRangeConstructorRejectsMixedVersions() throws {\n        #expect(throws: CIDR.Error.self) {\n            let v4 = try IPAddress(\"192.168.1.0\")\n            let v6 = try IPAddress(\"2001:db8::1\")\n            _ = try CIDR(lower: v4, upper: v6)\n        }\n    }\n\n    @Test func testRangeConstructorRejectsDifferentZones() throws {\n        #expect(throws: CIDR.Error.self) {\n            let lower = IPAddress.v6(try IPv6Address(\"fe80::1%eth0\"))\n            let upper = IPAddress.v6(try IPv6Address(\"fe80::2%eth1\"))\n            _ = try CIDR(lower: lower, upper: upper)\n        }\n    }\n\n    // MARK: - Validation Tests\n\n    struct InvalidInput {\n        let input: String\n    }\n\n    @Test(arguments: [\n        InvalidInput(input: \"192.168.1.0/33\"),  // IPv4 prefix too large\n        InvalidInput(input: \"2001:db8::/129\"),  // IPv6 prefix too large\n        InvalidInput(input: \"192.168.1.0\"),  // Missing prefix\n        InvalidInput(input: \"192.168.1.0/\"),  // Empty prefix\n        InvalidInput(input: \"192.168.1.0/abc\"),  // Invalid prefix\n    ])\n    func testRejectsInvalidInput(testCase: InvalidInput) throws {\n        #expect(throws: CIDR.Error.self) {\n            try CIDR(testCase.input)\n        }\n    }\n\n    @Test func testRejectsInvalidRangeOrder() throws {\n        #expect(throws: CIDR.Error.self) {\n            let lower = try IPAddress(\"192.168.1.255\")\n            let upper = try IPAddress(\"192.168.1.0\")\n            _ = try CIDR(lower: lower, upper: upper)\n        }\n    }\n\n    // MARK: - Range Constructor Validation Tests\n\n    @Test func testRangeConstructorValidatesContainment() throws {\n        // This should work: 192.168.1.64 to 192.168.1.127 -> /26\n        let lower1 = try IPAddress(\"192.168.1.64\")\n        let upper1 = try IPAddress(\"192.168.1.127\")\n        let cidr1 = try CIDR(lower: lower1, upper: upper1)\n        #expect(cidr1.prefix.length == 26)\n        #expect(cidr1.contains(lower1))\n        #expect(cidr1.contains(upper1))\n    }\n\n    @Test func testRangeConstructorValidatesIPv6Containment() throws {\n        // Test IPv6 range containment validation\n        let lower = try IPAddress(\"2001:db8::1000\")\n        let upper = try IPAddress(\"2001:db8::1fff\")\n        let cidr = try CIDR(lower: lower, upper: upper)\n        #expect(cidr.contains(lower))\n        #expect(cidr.contains(upper))\n    }\n\n    // MARK: - Version-Specific Range Constructors\n\n    @Test func testV4RangeConstructor() throws {\n        let lower = try IPAddress(\"192.168.1.0\")\n        let upper = try IPAddress(\"192.168.1.255\")\n        let cidr = try CIDR(lower: lower, upper: upper)\n        #expect(cidr.prefix.length == 24)\n        #expect(cidr.address.description == \"192.168.1.0\")\n    }\n\n    @Test func testV4RangeSingleAddress() throws {\n        let addr = try IPAddress(\"192.168.1.100\")\n        let cidr = try CIDR(lower: addr, upper: addr)\n        #expect(cidr.prefix.length == 32)\n        #expect(cidr.address.description == \"192.168.1.100\")\n    }\n\n    @Test func testV6RangeConstructor() throws {\n        let lower = try IPAddress(\"2001:db8::\")\n        let upper = try IPAddress(\"2001:db8::ffff:ffff:ffff:ffff\")\n        let cidr = try CIDR(lower: lower, upper: upper)\n        #expect(cidr.prefix.length == 64)\n        #expect(cidr.address.description == \"2001:db8::\")\n    }\n\n    @Test func testV6RangeSingleAddress() throws {\n        let addr = try IPAddress(\"2001:db8::1\")\n        let cidr = try CIDR(lower: addr, upper: addr)\n        #expect(cidr.prefix.length == 128)\n        #expect(cidr.address.description == \"2001:db8::1\")\n    }\n\n    @Test func testV4RangeRejectsInvalidOrder() throws {\n        let lower = try IPAddress(\"192.168.1.255\")\n        let upper = try IPAddress(\"192.168.1.0\")\n        #expect(throws: CIDR.Error.self) {\n            _ = try CIDR(lower: lower, upper: upper)\n        }\n    }\n\n    @Test func testV6RangeRejectsInvalidOrder() throws {\n        let lower = try IPAddress(\"2001:db8::ffff\")\n        let upper = try IPAddress(\"2001:db8::1\")\n        #expect(throws: CIDR.Error.self) {\n            _ = try CIDR(lower: lower, upper: upper)\n        }\n    }\n\n    @Test func testV6RangeRejectsDifferentZones() throws {\n        let lower = try IPAddress(\"fe80::1%eth0\")\n        let upper = try IPAddress(\"fe80::2%eth1\")\n        #expect(throws: CIDR.Error.self) {\n            _ = try CIDR(lower: lower, upper: upper)\n        }\n    }\n\n    // MARK: - Description Tests\n\n    @Test func testDescriptionFormat() throws {\n        let cidr = try CIDR(\"10.0.0.0/8\")\n        #expect(cidr.description == \"10.0.0.0/8\")\n    }\n\n    @Test func testPreservesAddress() throws {\n        let cidr = try CIDR(\"192.168.1.100/24\")\n        #expect(cidr.description == \"192.168.1.100/24\")\n    }\n\n    @Test(\n        \"CIDRv4 Codable encodes to string representation\",\n        arguments: [\n            \"192.168.1.0/24\",\n            \"10.0.0.0/8\",\n            \"172.16.0.0/12\",\n        ]\n    )\n    func testCIDRv4CodableEncode(cidr: String) throws {\n        let original = try CIDRv4(cidr)\n        let encoded = try JSONEncoder().encode(original)\n        let jsonString = String(data: encoded, encoding: .utf8)!\n        #expect(jsonString.contains(original.address.description))\n        #expect(jsonString.contains(\"\\(original.prefix.length)\"))\n    }\n\n    @Test(\n        \"CIDRv4 Codable decodes from string representation\",\n        arguments: [\n            \"192.168.1.0/24\",\n            \"10.0.0.0/8\",\n            \"172.16.0.0/12\",\n        ]\n    )\n    func testCIDRv4CodableDecode(cidr: String) throws {\n        let json = Data(\"\\\"\\(cidr)\\\"\".utf8)\n        let decoded = try JSONDecoder().decode(CIDRv4.self, from: json)\n        let expected = try CIDRv4(cidr)\n        #expect(decoded == expected)\n    }\n\n    @Test(\n        \"CIDRv6 Codable encodes to string representation\",\n        arguments: [\n            (\"2001:db8::/32\", \"2001:db8::\", 32),\n            (\"fe80::/10\", \"fe80::\", 10),\n            (\"::1/128\", \"::1\", 128),\n        ]\n    )\n    func testCIDRv6CodableEncode(cidr: String, expectedAddr: String, expectedPrefix: UInt8) throws {\n        let original = try CIDRv6(cidr)\n        let encoded = try JSONEncoder().encode(original)\n        let jsonString = String(data: encoded, encoding: .utf8)!\n        #expect(jsonString.contains(expectedAddr))\n        #expect(jsonString.contains(\"\\(expectedPrefix)\"))\n    }\n\n    @Test(\n        \"CIDRv6 Codable decodes from string representation\",\n        arguments: [\n            \"2001:db8::/32\",\n            \"fe80::/10\",\n            \"::1/128\",\n        ]\n    )\n    func testCIDRv6CodableDecode(cidr: String) throws {\n        let json = Data(\"\\\"\\(cidr)\\\"\".utf8)\n        let decoded = try JSONDecoder().decode(CIDRv6.self, from: json)\n        let expected = try CIDRv6(cidr)\n        #expect(decoded == expected)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationExtrasTests/TestIPAddress.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationExtras\n\n@Suite(\"Unified IPAddress Tests\")\nstruct IPAddressTests {\n\n    @Test(\n        \"Parse IPv4 addresses\",\n        arguments: [\n            \"192.168.1.1\",\n            \"127.0.0.1\",\n            \"0.0.0.0\",\n            \"255.255.255.255\",\n        ]\n    )\n    func testParseIPv4(input: String) throws {\n        let ip = try IPAddress(input)\n        #expect(ip.isV4, \"Should be IPv4: \\(input)\")\n        #expect(!ip.isV6, \"Should not be IPv6: \\(input)\")\n        #expect(ip.ipv4 != nil, \"Should have IPv4 value: \\(input)\")\n        #expect(ip.ipv6 == nil, \"Should not have IPv6 value: \\(input)\")\n    }\n\n    @Test(\n        \"Parse IPv6 addresses\",\n        arguments: [\n            \"2001:db8::1\",\n            \"::1\",\n            \"::\",\n            \"fe80::1\",\n            \"2001:db8:0:0:0:0:0:1\",\n        ]\n    )\n    func testParseIPv6(input: String) throws {\n        let ip = try IPAddress(input)\n        #expect(ip.isV6, \"Should be IPv6: \\(input)\")\n        #expect(!ip.isV4, \"Should not be IPv4: \\(input)\")\n        #expect(ip.ipv6 != nil, \"Should have IPv6 value: \\(input)\")\n        #expect(ip.ipv4 == nil, \"Should not have IPv4 value: \\(input)\")\n    }\n\n    @Test(\n        \"Loopback detection\",\n        arguments: [\n            // Loopback addresses\n            (\"127.0.0.1\", true, \"IPv4 loopback\"),\n            (\"127.0.0.255\", true, \"IPv4 loopback variant\"),\n            (\"127.255.255.255\", true, \"Any 127.x.x.x\"),\n            (\"::1\", true, \"IPv6 loopback\"),\n            // Non-loopback addresses\n            (\"192.168.1.1\", false, \"Private IPv4\"),\n            (\"2001:db8::1\", false, \"IPv6 documentation\"),\n            (\"0.0.0.0\", false, \"IPv4 unspecified\"),\n            (\"::\", false, \"IPv6 unspecified\"),\n        ]\n    )\n    func testLoopback(input: String, expected: Bool, description: String) throws {\n        let ip = try IPAddress(input)\n        #expect(ip.isLoopback == expected, \"\\(description): \\(input) should\\(expected ? \"\" : \" not\") be loopback\")\n    }\n\n    @Test(\n        \"Multicast detection\",\n        arguments: [\n            // Multicast addresses\n            (\"224.0.0.1\", true, \"IPv4 multicast start\"),\n            (\"239.255.255.255\", true, \"IPv4 multicast end (224.0.0.0/4)\"),\n            (\"ff02::1\", true, \"IPv6 link-local multicast\"),\n            (\"ff00::1\", true, \"IPv6 multicast\"),\n            // Non-multicast addresses\n            (\"192.168.1.1\", false, \"Private IPv4\"),\n            (\"2001:db8::1\", false, \"IPv6 documentation\"),\n            (\"223.255.255.255\", false, \"Just before multicast range\"),\n        ]\n    )\n    func testMulticast(input: String, expected: Bool, description: String) throws {\n        let ip = try IPAddress(input)\n        #expect(ip.isMulticast == expected, \"\\(description): \\(input) should\\(expected ? \"\" : \" not\") be multicast\")\n    }\n\n    @Test(\n        \"Unspecified detection\",\n        arguments: [\n            // Unspecified addresses\n            (\"0.0.0.0\", true, \"IPv4 unspecified\"),\n            (\"::\", true, \"IPv6 unspecified\"),\n            // Specified addresses\n            (\"0.0.0.1\", false, \"Not unspecified IPv4\"),\n            (\"192.168.1.1\", false, \"Private IPv4\"),\n            (\"::1\", false, \"IPv6 loopback\"),\n            (\"2001:db8::1\", false, \"IPv6 documentation\"),\n        ]\n    )\n    func testUnspecified(input: String, expected: Bool, description: String) throws {\n        let ip = try IPAddress(input)\n        #expect(ip.isUnspecified == expected, \"\\(description): \\(input) should\\(expected ? \"\" : \" not\") be unspecified\")\n    }\n\n    @Test(\"Comparable - IPv4 ordering\")\n    func testIPv4Ordering() throws {\n        let ip1 = try IPv4Address(\"192.168.1.1\")\n        let ip2 = try IPv4Address(\"192.168.1.2\")\n        let ip3 = try IPv4Address(\"192.168.2.1\")\n\n        #expect(ip1 < ip2)\n        #expect(ip2 < ip3)\n        #expect(ip1 < ip3)\n        #expect(!(ip2 < ip1))\n    }\n\n    @Test(\"Comparable - IPv6 ordering\")\n    func testIPv6Ordering() throws {\n        let ip1 = try IPv6Address(\"2001:db8::1\")\n        let ip2 = try IPv6Address(\"2001:db8::2\")\n        let ip3 = try IPv6Address(\"2001:db9::1\")\n\n        #expect(ip1 < ip2)\n        #expect(ip2 < ip3)\n        #expect(ip1 < ip3)\n        #expect(!(ip2 < ip1))\n    }\n\n    @Test(\n        \"Equality\",\n        arguments: [\n            (\"192.168.1.1\", \"192.168.1.1\", true, \"Same IPv4\"),\n            (\"192.168.1.1\", \"192.168.1.2\", false, \"Different IPv4\"),\n            (\"2001:db8::1\", \"2001:0db8:0000:0000:0000:0000:0000:0001\", true, \"Same IPv6, different format\"),\n            (\"2001:db8::1\", \"2001:db8::2\", false, \"Different IPv6\"),\n        ]\n    )\n    func testEquality(addr1: String, addr2: String, shouldBeEqual: Bool, description: String) throws {\n        let ip1 = try IPAddress(addr1)\n        let ip2 = try IPAddress(addr2)\n\n        if shouldBeEqual {\n            #expect(ip1 == ip2, \"\\(description): \\(addr1) should equal \\(addr2)\")\n        } else {\n            #expect(ip1 != ip2, \"\\(description): \\(addr1) should not equal \\(addr2)\")\n        }\n    }\n\n    @Test(\"Hashable\")\n    func testHashable() throws {\n        var dict: [IPAddress: String] = [:]\n\n        let ip1 = try IPAddress(\"192.168.1.1\")\n        let ip2 = try IPAddress(\"2001:db8::1\")\n\n        dict[ip1] = \"IPv4\"\n        dict[ip2] = \"IPv6\"\n\n        #expect(dict[ip1] == \"IPv4\")\n        #expect(dict[ip2] == \"IPv6\")\n        #expect(dict.count == 2)\n    }\n\n    @Test(\n        \"Codable encodes to string representation\",\n        arguments: [\n            \"127.0.0.1\",\n            \"192.168.1.1\",\n            \"0.0.0.0\",\n            \"255.255.255.255\",\n        ]\n    )\n    func testCodableEncodeIPv4(address: String) throws {\n        let original = try IPAddress(address)\n        let encoded = try JSONEncoder().encode(original)\n        #expect(String(data: encoded, encoding: .utf8) == \"\\\"\\(address)\\\"\")\n    }\n\n    @Test(\n        \"Codable decodes from string representation\",\n        arguments: [\n            \"127.0.0.1\",\n            \"192.168.1.1\",\n            \"0.0.0.0\",\n            \"255.255.255.255\",\n        ]\n    )\n    func testCodableDecodeIPv4(address: String) throws {\n        let json = Data(\"\\\"\\(address)\\\"\".utf8)\n        let decoded = try JSONDecoder().decode(IPAddress.self, from: json)\n        let expected = try IPAddress(address)\n        #expect(decoded == expected)\n    }\n\n    @Test(\n        \"Codable encodes to string representation\",\n        arguments: [\n            (\"::1\", \"::1\"),\n            (\"2001:db8::1\", \"2001:db8::1\"),\n            (\"::\", \"::\"),\n            (\"fe80::1\", \"fe80::1\"),\n        ]\n    )\n    func testCodableEncodeIPv6(input: String, expected: String) throws {\n        let original = try IPAddress(input)\n        let encoded = try JSONEncoder().encode(original)\n        #expect(String(data: encoded, encoding: .utf8) == \"\\\"\\(expected)\\\"\")\n    }\n\n    @Test(\n        \"Codable decodes from string representation\",\n        arguments: [\n            \"::1\",\n            \"2001:db8::1\",\n            \"::\",\n            \"fe80::1\",\n        ]\n    )\n    func testCodableDecodeIPv6(address: String) throws {\n        let json = Data(\"\\\"\\(address)\\\"\".utf8)\n        let decoded = try JSONDecoder().decode(IPAddress.self, from: json)\n        let expected = try IPAddress(address)\n        #expect(decoded == expected)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationExtrasTests/TestIPv4Address.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationExtras\n\n@Suite(\"IPv4Address Tests\")\nstruct IPv4AddressTests {\n\n    // MARK: - Initializer Tests\n\n    @Suite(\"Initializers\")\n    struct InitializerTests {\n\n        @Test(\n            \"UInt32 initializer\",\n            arguments: [\n                (0x7F00_0001, \"127.0.0.1\"),  // localhost\n                (0x0000_0000, \"0.0.0.0\"),  // zero address\n                (0xFFFF_FFFF, \"255.255.255.255\"),  // max address\n                (0xC0A8_0101, \"192.168.1.1\"),  // private network\n                (0x0808_0808, \"8.8.8.8\"),  // Google DNS\n            ]\n        )\n        func testUInt32Initializer(inputValue: UInt32, description: String) {\n            let address = IPv4Address(inputValue)\n            #expect(address.value == inputValue)\n        }\n\n        @Test(\n            \"String initializer - valid addresses\",\n            arguments: [\n                (\"127.0.0.1\", 0x7F00_0001),  // localhost\n                (\"0.0.0.0\", 0x0000_0000),  // zero address\n                (\"255.255.255.255\", 0xFFFF_FFFF),  // broadcast\n                (\"10.0.0.1\", 0x0A00_0001),  // private network 10.x\n                (\"192.168.1.1\", 0xC0A8_0101),  // private network 192.168.x\n                (\"172.16.0.1\", 0xAC10_0001),  // private network 172.16.x\n                (\"1.2.3.4\", 0x0102_0304),  // single digits\n                (\"192.168.100.254\", 0xC0A8_64FE),  // mixed digits\n            ]\n        )\n        func testStringInitializerValid(addressString: String, expectedValue: UInt32) throws {\n            let address = try IPv4Address(addressString)\n            #expect(address.value == expectedValue)\n        }\n\n        @Test(\n            \"String initializer - invalid addresses\",\n            arguments: [\n                \"\",  // empty string\n                \"1.2.3\",  // too short\n                \"1.2.3.4.5\",  // too many octets\n                \"192.168.1.256\",  // octet out of range\n                \"192.168.001.1\",  // leading zeros\n                \"01.2.3.4\",  // leading zero first octet\n                \" 192.168.1.1\",  // leading whitespace\n                \"192.168.1.1 \",  // trailing whitespace\n                \"192. 168.1.1\",  // internal whitespace\n                \"192.168.1.a\",  // invalid character\n                \"192.168.1.-1\",  // negative number\n                \"192..1.1\",  // missing octet\n                \".168.1.1\",  // missing first octet\n                \"192.168.1.\",  // missing last octet\n                \"192.168.1.1.extra\",  // too long\n            ]\n        )\n        func testStringInitializerInvalid(invalidAddress: String) {\n            #expect(throws: AddressError.self) {\n                try IPv4Address(invalidAddress)\n            }\n        }\n    }\n\n    // MARK: - Property Tests\n\n    @Suite(\"Properties\")\n    struct PropertyTests {\n\n        @Test(\n            \"bytes property\",\n            arguments: [\n                (UInt32(0x7F00_0001), [UInt8(127), UInt8(0), UInt8(0), UInt8(1)]),  // localhost\n                (UInt32(0x0000_0000), [UInt8(0), UInt8(0), UInt8(0), UInt8(0)]),  // zero\n                (UInt32(0xFFFF_FFFF), [UInt8(255), UInt8(255), UInt8(255), UInt8(255)]),  // broadcast\n                (UInt32(0xC0A8_0101), [UInt8(192), UInt8(168), UInt8(1), UInt8(1)]),  // private network\n                (UInt32(0x1234_5678), [UInt8(0x12), UInt8(0x34), UInt8(0x56), UInt8(0x78)]),  // byte order test\n            ]\n        )\n        func testBytesProperty(inputValue: UInt32, expectedBytes: [UInt8]) {\n            let address = IPv4Address(inputValue)\n            #expect(address.bytes == expectedBytes)\n        }\n\n        @Test(\n            \"description property\",\n            arguments: [\n                (0x7F00_0001, \"127.0.0.1\"),  // localhost\n                (0x0000_0000, \"0.0.0.0\"),  // zero\n                (0xFFFF_FFFF, \"255.255.255.255\"),  // broadcast\n                (0xC0A8_0101, \"192.168.1.1\"),  // private network\n                (0x0102_0304, \"1.2.3.4\"),  // single digits\n            ]\n        )\n        func testDescriptionProperty(inputValue: UInt32, expectedDescription: String) {\n            let address = IPv4Address(inputValue)\n            #expect(address.description == expectedDescription)\n        }\n\n        @Test(\n            \"round-trip string conversion\",\n            arguments: [\n                \"0.0.0.0\",\n                \"127.0.0.1\",\n                \"192.168.1.1\",\n                \"10.0.0.1\",\n                \"172.16.0.1\",\n                \"255.255.255.255\",\n                \"1.2.3.4\",\n                \"8.8.8.8\",\n                \"1.1.1.1\",\n            ]\n        )\n        func testRoundTripStringConversion(addressString: String) throws {\n            let address = try IPv4Address(addressString)\n            #expect(address.description == addressString)\n        }\n    }\n\n    // MARK: - Protocol Conformance Tests\n\n    @Suite(\"Protocol Conformances\")\n    struct ProtocolConformanceTests {\n\n        @Test(\"Equatable conformance\")\n        func testEquatableConformance() {\n            let addr1 = IPv4Address(0x7F00_0001)\n            let addr2 = IPv4Address(0x7F00_0001)\n            let addr3 = IPv4Address(0xC0A8_0101)\n\n            #expect(addr1 == addr2)\n            #expect(addr1 != addr3)\n            #expect(addr2 != addr3)\n        }\n\n        @Test(\"Hashable conformance\")\n        func testHashableConformance() {\n            let addr1 = IPv4Address(0x7F00_0001)\n            let addr2 = IPv4Address(0x7F00_0001)\n            let addr3 = IPv4Address(0xC0A8_0101)\n\n            // Equal objects should have equal hash values\n            #expect(addr1.hashValue == addr2.hashValue)\n\n            // Different objects should ideally have different hash values\n            // (though this is not guaranteed, it's very likely for these values)\n            #expect(addr1.hashValue != addr3.hashValue)\n\n            // Test that addresses can be used in Sets and Dictionaries\n            let addressSet: Set<IPv4Address> = [addr1, addr2, addr3]\n            #expect(addressSet.count == 2)  // addr1 and addr2 are equal\n\n            let addressDict = [addr1: \"localhost\", addr3: \"private\"]\n            #expect(addressDict[addr2] == \"localhost\")  // addr2 equals addr1\n        }\n\n        @Test(\"CustomStringConvertible conformance\")\n        func testCustomStringConvertibleConformance() {\n            let address = IPv4Address(0x7F00_0001)\n            let stringRepresentation = String(describing: address)\n            #expect(stringRepresentation == \"127.0.0.1\")\n        }\n\n        @Test(\"Sendable conformance\")\n        func testSendableConformance() {\n            // This test verifies that IPv4Address can be safely passed across concurrency boundaries\n            let address = IPv4Address(0x7F00_0001)\n\n            Task {\n                let taskAddress = address\n                #expect(taskAddress.value == 0x7F00_0001)\n            }\n        }\n\n        @Test(\n            \"Codable encodes to string representation\",\n            arguments: [\n                \"127.0.0.1\",\n                \"192.168.1.1\",\n                \"0.0.0.0\",\n                \"255.255.255.255\",\n            ]\n        )\n        func testCodableEncode(address: String) throws {\n            let original = try IPv4Address(address)\n            let encoded = try JSONEncoder().encode(original)\n            #expect(String(data: encoded, encoding: .utf8) == \"\\\"\\(address)\\\"\")\n        }\n\n        @Test(\n            \"Codable decodes from string representation\",\n            arguments: [\n                \"127.0.0.1\",\n                \"192.168.1.1\",\n                \"0.0.0.0\",\n                \"255.255.255.255\",\n            ]\n        )\n        func testCodableDecode(address: String) throws {\n            let json = Data(\"\\\"\\(address)\\\"\".utf8)\n            let decoded = try JSONDecoder().decode(IPv4Address.self, from: json)\n            let expected = try IPv4Address(address)\n            #expect(decoded == expected)\n        }\n    }\n\n    // MARK: - Edge Cases and Error Conditions\n\n    @Suite(\"Edge Cases\")\n    struct EdgeCaseTests {\n\n        @Test(\n            \"boundary values\",\n            arguments: [\n                (\"0.0.0.0\", 0x0000_0000),  // minimum\n                (\"255.255.255.255\", 0xFFFF_FFFF),  // maximum\n                (\"255.0.0.0\", 0xFF00_0000),  // max first octet\n                (\"0.255.0.0\", 0x00FF_0000),  // max second octet\n                (\"0.0.255.0\", 0x0000_FF00),  // max third octet\n                (\"0.0.0.255\", 0x0000_00FF),  // max fourth octet\n            ]\n        )\n        func testBoundaryValues(addressString: String, expectedValue: UInt32) throws {\n            let address = try IPv4Address(addressString)\n            #expect(address.value == expectedValue)\n        }\n\n        @Test(\n            \"special addresses\",\n            arguments: [\n                \"127.0.0.1\",  // loopback\n                \"255.255.255.255\",  // broadcast\n                \"0.0.0.0\",  // network address\n                \"8.8.8.8\",  // Google DNS\n                \"1.1.1.1\",  // Cloudflare DNS\n            ]\n        )\n        func testSpecialAddresses(addressString: String) throws {\n            let address = try IPv4Address(addressString)\n            #expect(address.description == addressString)\n        }\n\n        @Test(\n            \"leading zero validation - invalid\",\n            arguments: [\n                \"01.0.0.0\",\n                \"0.01.0.0\",\n                \"0.0.01.0\",\n                \"0.0.0.01\",\n                \"192.168.001.1\",\n                \"010.0.0.1\",\n                \"00.0.0.1\",\n            ]\n        )\n        func testLeadingZeroValidationInvalid(invalidAddress: String) {\n            #expect(throws: AddressError.self) {\n                try IPv4Address(invalidAddress)\n            }\n        }\n\n        @Test(\"leading zero validation - valid single zeros\")\n        func testLeadingZeroValidationValid() {\n            // Single \"0\" should be valid\n            #expect(throws: Never.self) {\n                try IPv4Address(\"0.0.0.0\")\n            }\n        }\n\n        @Test(\n            \"string length validation - too short\",\n            arguments: [\n                \"\", \"1\", \"1.2\", \"1.2.3\", \"1.2.3.\",\n            ]\n        )\n        func testStringLengthValidationTooShort(shortString: String) {\n            #expect(throws: AddressError.self) {\n                try IPv4Address(shortString)\n            }\n        }\n\n        @Test(\n            \"string length validation - too long\",\n            arguments: [\n                \"255.255.255.255.1\",\n                \"1234.168.1.1\",\n                \"192.1234.1.1\",\n                \"192.168.1234.1\",\n                \"192.168.1.1234\",\n            ]\n        )\n        func testStringLengthValidationTooLong(longString: String) {\n            #expect(throws: AddressError.self) {\n                try IPv4Address(longString)\n            }\n        }\n    }\n\n    // MARK: - Performance Tests\n\n    @Suite(\"Performance\")\n    struct PerformanceTests {\n\n        @Test(\"parsing performance\")\n        func testParsingPerformance() throws {\n            let testAddresses = [\n                \"192.168.1.1\",\n                \"10.0.0.1\",\n                \"172.16.0.1\",\n                \"127.0.0.1\",\n                \"8.8.8.8\",\n                \"1.1.1.1\",\n                \"255.255.255.255\",\n                \"0.0.0.0\",\n            ]\n\n            // Warm up\n            for _ in 0..<100 {\n                for address in testAddresses {\n                    _ = try IPv4Address(address)\n                }\n            }\n\n            // Measure performance\n            let iterations = 10000\n            let startTime = Date()\n\n            for _ in 0..<iterations {\n                for address in testAddresses {\n                    _ = try IPv4Address(address)\n                }\n            }\n\n            let endTime = Date()\n            let totalTime = endTime.timeIntervalSince(startTime)\n            let averageTime = totalTime / Double(iterations * testAddresses.count)\n\n            // Should be very fast - less than 1ms per parse on average\n            #expect(averageTime < 0.001, \"Parsing should be fast: \\(averageTime)s per address\")\n        }\n\n        @Test(\"bytes property performance\")\n        func testBytesPropertyPerformance() {\n            let address = IPv4Address(0xC0A8_0101)\n\n            let iterations = 100000\n            let startTime = Date()\n\n            for _ in 0..<iterations {\n                _ = address.bytes\n            }\n\n            let endTime = Date()\n            let totalTime = endTime.timeIntervalSince(startTime)\n            let averageTime = totalTime / Double(iterations)\n\n            // Should be very fast - less than 0.1ms per call on average\n            #expect(averageTime < 0.0001, \"Bytes property should be fast: \\(averageTime)s per call\")\n        }\n\n        @Test(\"description property performance\")\n        func testDescriptionPropertyPerformance() {\n            let address = IPv4Address(0xC0A8_0101)\n\n            let iterations = 10000\n            let startTime = Date()\n\n            for _ in 0..<iterations {\n                _ = address.description\n            }\n\n            let endTime = Date()\n            let totalTime = endTime.timeIntervalSince(startTime)\n            let averageTime = totalTime / Double(iterations)\n\n            // Should be reasonably fast - less than 1ms per call on average\n            #expect(averageTime < 0.001, \"Description property should be fast: \\(averageTime)s per call\")\n        }\n    }\n\n    // MARK: - Integration Tests\n\n    @Suite(\"Integration\")\n    struct IntegrationTests {\n\n        @Test(\n            \"comprehensive round-trip test\",\n            arguments: [\n                (0x0000_0000, \"0.0.0.0\"),\n                (0x7F00_0001, \"127.0.0.1\"),\n                (0xC0A8_0101, \"192.168.1.1\"),\n                (0x0A00_0001, \"10.0.0.1\"),\n                (0xAC10_0001, \"172.16.0.1\"),\n                (0xFFFF_FFFF, \"255.255.255.255\"),\n                (0x0808_0808, \"8.8.8.8\"),\n                (0x0101_0101, \"1.1.1.1\"),\n                (0x1234_5678, \"18.52.86.120\"),\n                (0xDEAD_BEEF, \"222.173.190.239\"),\n            ]\n        )\n        func testComprehensiveRoundTrip(expectedValue: UInt32, expectedString: String) throws {\n            // Test UInt32 -> String\n            let addressFromUInt32 = IPv4Address(expectedValue)\n            #expect(addressFromUInt32.description == expectedString)\n\n            // Test String -> UInt32\n            let addressFromString = try IPv4Address(expectedString)\n            #expect(addressFromString.value == expectedValue)\n\n            // Test equality\n            #expect(addressFromUInt32 == addressFromString)\n        }\n\n        @Test(\n            \"error message consistency\",\n            arguments: [\n                \"\",\n                \"256.1.1.1\",\n                \"1.2.3\",\n                \"1.2.3.4.5\",\n                \"192.168.001.1\",\n                \" 192.168.1.1\",\n                \"192.168.1.1 \",\n                \"192.168.1.a\",\n            ]\n        )\n        func testErrorMessageConsistency(invalidInput: String) {\n            do {\n                _ = try IPv4Address(invalidInput)\n                #expect(Bool(false), \"Should have thrown for input: \\(invalidInput)\")\n            } catch let error as AddressError {\n                #expect(error == AddressError.unableToParse)\n            } catch {\n                #expect(Bool(false), \"Should have thrown AddressError, got: \\(error)\")\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationExtrasTests/TestIPv6Address+Parse.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationExtras\n\n@Suite(\"IPv6Address Parsing Tests\")\nstruct IPv6AddressParseTests {\n\n    // MARK: - Valid Hexadecimal Group Tests\n\n    @Test(\n        \"Parsing valid hexadecimal groups\",\n        arguments: [\n            (\"0\", 0x0),\n            (\"1\", 0x1),\n            (\"F\", 0xF),\n            (\"FF\", 0xFF),\n            (\"FFF\", 0xFFF),\n            (\"FFFF\", 0xFFFF),\n            (\"1234\", 0x1234),\n            (\"abcd\", 0xABCD),\n            (\"ABCD\", 0xABCD),\n            (\"0000\", 0x0000),\n        ]\n    )\n    func testParseValidHexadecimalGroups(input: String, expectedValue: UInt16) throws {\n        let utf8 = input.utf8\n        let (parsedValue, nextIndex) = try IPv6Address.parseHexadecimal(\n            from: utf8,\n            startingAt: utf8.startIndex\n        )\n\n        #expect(\n            parsedValue == expectedValue,\n            \"For input '\\(input)': expected \\(expectedValue) but got \\(parsedValue)\"\n        )\n        #expect(nextIndex == input.endIndex, \"Parser should consume entire input\")\n    }\n\n    @Test(\n        \"Parsing hexadecimal groups with trailing characters\",\n        arguments: [\n            (\"FF:1234\", 0xFF, \":1234\"),\n            (\"1234G\", 0x1234, \"G\"),\n            (\"AB::CD\", 0xAB, \"::CD\"),\n            (\"0Z\", 0x0, \"Z\"),\n        ]\n    )\n    func testParseHexadecimalGroupWithTrailingCharacters(\n        input: String,\n        expectedValue: UInt16,\n        expectedRemainder: String\n    ) throws {\n        let utf8 = input.utf8\n        let (parsedValue, nextIndex) = try IPv6Address.parseHexadecimal(\n            from: utf8,\n            startingAt: utf8.startIndex\n        )\n\n        let remainder = String(input[String.Index(nextIndex, within: input)!...])\n\n        #expect(\n            parsedValue == expectedValue,\n            \"For input '\\(input)': expected \\(expectedValue) but got \\(parsedValue)\"\n        )\n        #expect(\n            remainder == expectedRemainder,\n            \"For input '\\(input)': expected remainder '\\(expectedRemainder)' but got '\\(remainder)'\"\n        )\n    }\n\n    // MARK: - Error Handling Tests\n\n    @Test(\n        \"Parsing invalid hexadecimal groups should throw\",\n        arguments: [\n            \"\",  // Empty string - no hex digits found\n            \"G\",  // Invalid hex character - no hex digits found\n            \"GGGG\",  // All invalid hex characters - no hex digits found\n        ]\n    )\n    func testParseInvalidHexadecimalGroup(invalidInput: String) {\n        #expect(throws: AddressError.self) {\n            let utf8 = invalidInput.utf8\n            _ = try IPv6Address.parseHexadecimal(\n                from: utf8,\n                startingAt: utf8.startIndex\n            )\n        }\n    }\n\n    @Test(\n        \"Parsing hexadecimal groups with overflow behavior\",\n        arguments: [\n            (\"12345\", 0x1234, \"5\"),  // 5 digits -> takes first 4\n            (\"10000\", 0x1000, \"0\"),  // 5 digits -> takes first 4\n            (\"FFFFF\", 0xFFFF, \"F\"),  // 5 digits -> takes first 4\n            (\"123456789\", 0x1234, \"56789\"),  // Many digits -> takes first 4\n        ]\n    )\n    func testParseHexadecimalGroupOverflow(\n        input: String,\n        expectedValue: UInt16,\n        expectedRemainder: String\n    ) throws {\n        let utf8 = input.utf8\n        let (parsedValue, nextIndex) = try IPv6Address.parseHexadecimal(\n            from: utf8,\n            startingAt: utf8.startIndex\n        )\n\n        let remainder = String(input[String.Index(nextIndex, within: input)!...])\n\n        #expect(\n            parsedValue == expectedValue,\n            \"For input '\\(input)': expected \\(expectedValue) but got \\(parsedValue)\"\n        )\n        #expect(\n            remainder == expectedRemainder,\n            \"For input '\\(input)': expected remainder '\\(expectedRemainder)' but got '\\(remainder)'\"\n        )\n    }\n\n    @Test(\"Parsing from middle of string\")\n    func testParseHexadecimalGroupFromMiddle() throws {\n        let input = \"prefix1234suffix\"\n        let utf8 = input.utf8\n        let startIndex = utf8.index(utf8.startIndex, offsetBy: 6)  // Start at \"1234\"\n\n        let (parsedValue, nextIndex) = try IPv6Address.parseHexadecimal(\n            from: utf8,\n            startingAt: startIndex\n        )\n\n        #expect(parsedValue == 0x1234)\n\n        let remainder = String(input[String.Index(nextIndex, within: input)!...])\n        #expect(remainder == \"suffix\")\n    }\n\n    // MARK: - Performance Tests\n\n    @Test(\"Performance with maximum length hex groups\")\n    func testParsePerformance() throws {\n        let testInput = \"FFFF\"\n\n        // Measure performance of parsing operation\n        let startTime = CFAbsoluteTimeGetCurrent()\n        let count = 10000\n        for _ in 0..<count {\n            let utf8 = testInput.utf8\n            _ = try IPv6Address.parseHexadecimal(\n                from: utf8,\n                startingAt: utf8.startIndex\n            )\n        }\n\n        let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime\n\n        // Expect parsing to be reasonably fast (less than 1ms per operation on average)\n        print(\"Parsed \\(count) IPv6 addresses in \\(timeElapsed)s\")\n        #expect(timeElapsed < 0.1, \"Parsing should be performant: \\(timeElapsed)s for 10000 operations\")\n    }\n\n    // MARK: - RFC 4291 Section 2.3 Text Representation of Address Prefixes Tests\n\n    @Test(\"RFC 4291 Section 2.3 - Valid address prefix representations\")\n    func testRFC4291Section23ValidPrefixRepresentations() throws {\n        // Examples of valid 60-bit prefix 20010DB80000CD3 (hexadecimal)\n        let validPrefixCases = [\n            \"2001:0DB8:0000:CD30:0000:0000:0000:0000/60\",\n            \"2001:0DB8::CD30:0:0:0:0/60\",\n            \"2001:0DB8:0:CD30::/60\",\n        ]\n\n        let parsed = try validPrefixCases.map { testCase in\n            let addressPart = String(testCase.prefix(while: { $0 != \"/\" }))\n            return try IPv6Address.parse(addressPart).bytes\n        }\n\n        #expect(\n            parsed.allSatisfy { $0 == parsed.first },\n            \"All valid prefix representations should parse to identical byte arrays\"\n        )\n    }\n\n    @Test(\"RFC 4291 Section 2.3 - Invalid address prefix representations\")\n    func testRFC4291Section23InvalidPrefixRepresentations() throws {\n        // RFC 4291 Section 2.3 examples of invalid representations\n        let invalidPrefixCases = [\n            \"2001:0DB8:0:CD3/60\",  // ex 1: may drop leading zeros, but not trailing zeros\"\n            \"2001:0DB8::CD30/60\",  // ex 2: expands to wrong address\n            \"2001:0DB8::CD3/60\",  // ex 3: expands to wrong address\n        ]\n\n        let validAddress = \"2001:0DB8:0000:CD30:0000:0000:0000:0000\"\n        let parsedValidAddress: [UInt8] = [32, 1, 13, 184, 0, 0, 205, 48, 0, 0, 0, 0, 0, 0, 0, 0]\n        let parsedLibValidAddress = try IPv6Address.parse(validAddress).bytes\n        #expect(parsedLibValidAddress == parsedValidAddress)\n\n        for testCase in invalidPrefixCases {\n            let addressPart = String(testCase.prefix(while: { $0 != \"/\" }))\n\n            do {\n                let actualBytes = try IPv6Address.parse(addressPart).bytes\n                #expect(actualBytes != parsedValidAddress, \"\\(testCase) should not match valid prefix\")\n            } catch {\n                // If parsing fails, that's also acceptable for invalid representations\n                #expect(error is AddressError, \"\\(testCase) should throw IPAddressError if it fails to parse\")\n            }\n        }\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.3 - Leading zero rules in prefixes\",\n        arguments: [\n            (\"2001:DB8:0:0CD3::\", \"2001:DB8:0:CD3::\", \"Can drop leading zeros 0CD3 -> CD3\"),\n            (\"2001:0DB8::\", \"2001:DB8::\", \"Can drop leading zeros 0DB8 -> DB8\"),\n            (\"0001:0002:0003::\", \"1:2:3::\", \"Can drop leading zeros in multiple groups\"),\n        ]\n    )\n    func testRFC4291Section23LeadingZeroRules(form1: String, form2: String, description: String) throws {\n        let bytes1 = try IPv6Address.parse(form1).bytes\n        let bytes2 = try IPv6Address.parse(form2).bytes\n        #expect(bytes1 == bytes2, \"\\(description)\")\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.3 - cannot drop trailing zeros\",\n        arguments: [\n            (\"2001:DB8:0:CD30::\", \"2001:DB8:0:CD3::\", \"Cannot drop trailing zeros in CD30 -> CD3\"),\n            (\"2001:DB80::\", \"2001:DB8::\", \"Cannot drop trailing zero DB80 -> DB8\"),\n            (\"ABCD:EF00::\", \"ABCD:EF::\", \"Cannot drop trailing zeros EF00 -> EF\"),\n        ]\n    )\n    func testRFC4291Section23CannotDropTrailingZeros(full: String, truncated: String, description: String) throws {\n        let fullBytes = try IPv6Address.parse(full).bytes\n        let truncatedBytes = try IPv6Address.parse(truncated).bytes\n        #expect(fullBytes != truncatedBytes, \"\\(description)\")\n    }\n\n    @Test(\"RFC 4291 Section 2.3 - Node address and subnet prefix combination\")\n    func testRFC4291Section23NodeAddressSubnetCombination() throws {\n        // RFC example: node address 2001:0DB8:0:CD30:123:4567:89AB:CDEF\n        // and its subnet number 2001:0DB8:0:CD30::/60\n        // can be abbreviated as 2001:0DB8:0:CD30:123:4567:89AB:CDEF/60\n\n        let nodeAddress = \"2001:0DB8:0:CD30:123:4567:89AB:CDEF\"\n        let subnetPrefix = \"2001:0DB8:0:CD30::\"\n\n        // Both should parse successfully\n        #expect(throws: Never.self, \"Node address should parse successfully\") {\n            _ = try IPv6Address.parse(nodeAddress)\n        }\n\n        #expect(throws: Never.self, \"Subnet prefix should parse successfully\") {\n            _ = try IPv6Address.parse(subnetPrefix)\n        }\n\n        // Verify that the subnet prefix is indeed a prefix of the node address\n        let nodeBytes = try IPv6Address.parse(nodeAddress).bytes\n        let subnetBytes = try IPv6Address.parse(subnetPrefix).bytes\n\n        // Validate that node address has the subnet as a prefix (60-bit prefix = 7.5 bytes)\n        let prefixLength = 60\n        let hasValidPrefix = nodeBytes.hasPrefix(subnetBytes, upToBits: prefixLength)\n\n        #expect(\n            hasValidPrefix,\n            \"Node address should have subnet as a \\(prefixLength)-bit prefix\"\n        )\n    }\n\n    // MARK: - RFC 4291 Section 2.2 Comprehensive Text Representation Tests\n\n    @Test(\n        \"RFC 4291 Section 2.2 - Preferred form with all groups\",\n        arguments: [\n            \"2001:0db8:0000:0042:0000:8a2e:0370:7334\",\n            \"ABCD:EF01:2345:6789:ABCD:EF01:2345:6789\",\n            \"0000:0000:0000:0000:0000:0000:0000:0000\",\n            \"FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF\",\n        ]\n    )\n    func testRFC4291Section22PreferredForm(testCase: String) throws {\n        #expect(throws: Never.self, \"Should parse preferred form: \\(testCase)\") {\n            _ = try IPv6Address.parse(testCase)\n        }\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.2 - Leading zero omission in all positions\",\n        arguments: [\n            (\"2001:0db8:0000:0042:0000:8a2e:0370:7334\", \"2001:db8:0:42:0:8a2e:370:7334\"),\n            (\"0001:0002:0003:0004:0005:0006:0007:0008\", \"1:2:3:4:5:6:7:8\"),\n            (\"0000:0001:0002:0003:0004:0005:0006:0007\", \"0:1:2:3:4:5:6:7\"),\n            (\"1000:0100:0010:0001:1000:0100:0010:0001\", \"1000:100:10:1:1000:100:10:1\"),\n        ]\n    )\n    func testRFC4291Section22LeadingZeroOmission(full: String, compressed: String) throws {\n        let fullBytes = try IPv6Address.parse(full).bytes\n        let compressedBytes = try IPv6Address.parse(compressed).bytes\n        #expect(fullBytes == compressedBytes, \"'\\(full)' should equal '\\(compressed)'\")\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.2 - Zero compression at beginning\",\n        arguments: [\n            (\"::\", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),\n            (\"::1\", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]),\n            (\"::8a2e:370:7334\", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34]),\n        ]\n    )\n    func testRFC4291Section22ZeroCompressionAtBeginning(compressed: String, expected: [UInt8]) throws {\n        let parsed = try IPv6Address.parse(compressed)\n        #expect(parsed.bytes == expected, \"'\\(compressed)' should parse correctly\")\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.2 - Zero compression in middle\",\n        arguments: [\n            (\"2001:db8::8a2e:370:7334\", \"2001:db8:0:0:0:8a2e:370:7334\"),\n            (\"2001:db8::1\", \"2001:db8:0:0:0:0:0:1\"),\n            (\"fe80::1\", \"fe80:0:0:0:0:0:0:1\"),\n            (\"2001:0db8:0:0::1\", \"2001:db8:0:0:0:0:0:1\"),\n        ]\n    )\n    func testRFC4291Section22ZeroCompressionInMiddle(compressed: String, full: String) throws {\n        let compressedBytes = try IPv6Address.parse(compressed).bytes\n        let fullBytes = try IPv6Address.parse(full).bytes\n        #expect(compressedBytes == fullBytes, \"'\\(compressed)' should equal '\\(full)'\")\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.2 - Zero compression at end\",\n        arguments: [\n            (\"2001:db8::\", \"2001:db8:0:0:0:0:0:0\"),\n            (\"2001:db8:0:0:1::\", \"2001:db8:0:0:1:0:0:0\"),\n            (\"fe80::\", \"fe80:0:0:0:0:0:0:0\"),\n            (\"1::\", \"1:0:0:0:0:0:0:0\"),\n        ]\n    )\n    func testRFC4291Section22ZeroCompressionAtEnd(compressed: String, full: String) throws {\n        let compressedBytes = try IPv6Address.parse(compressed).bytes\n        let fullBytes = try IPv6Address.parse(full).bytes\n        #expect(compressedBytes == fullBytes, \"'\\(compressed)' should equal '\\(full)'\")\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.2 - Multiple :: should fail\",\n        arguments: [\n            \"2001::db8::1\",\n            \"::1::2\",\n            \"fe80::1::2::3\",\n            \"::1::\",\n        ]\n    )\n    func testRFC4291Section22MultipleDoubleColonsShouldFail(invalid: String) {\n        #expect(throws: AddressError.self, \"Multiple '::' should fail: \\(invalid)\") {\n            _ = try IPv6Address.parse(invalid)\n        }\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.2 - Case insensitivity\",\n        arguments: [\n            (\"2001:db8::1\", \"2001:DB8::1\", \"2001:Db8::1\"),\n            (\"dead:beef::cafe\", \"DEAD:BEEF::CAFE\", \"DeAd:BeEf::CaFe\"),\n            (\"fe80::1\", \"FE80::1\", \"Fe80::1\"),\n            (\"abcd:ef01:2345:6789::1\", \"ABCD:EF01:2345:6789::1\", \"AbCd:Ef01:2345:6789::1\"),\n        ]\n    )\n    func testRFC4291Section22CaseInsensitivity(lower: String, upper: String, mixed: String) throws {\n        let lowerBytes = try IPv6Address.parse(lower).bytes\n        let upperBytes = try IPv6Address.parse(upper).bytes\n        let mixedBytes = try IPv6Address.parse(mixed).bytes\n\n        #expect(lowerBytes == upperBytes, \"Case should not matter: '\\(lower)' vs '\\(upper)'\")\n        #expect(lowerBytes == mixedBytes, \"Case should not matter: '\\(lower)' vs '\\(mixed)'\")\n    }\n\n    @Test(\"RFC 4291 Section 2.2 - Special addresses\")\n    func testRFC4291Section22SpecialAddresses() throws {\n        // Unspecified address\n        let unspecified = try IPv6Address.parse(\"::\")\n        #expect(unspecified.bytes.allSatisfy { $0 == 0 }, \":: should be all zeros\")\n\n        // Loopback address\n        let loopback = try IPv6Address.parse(\"::1\")\n        let expectedLoopback: [UInt8] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]\n        #expect(loopback.bytes == expectedLoopback, \"::1 should be loopback address\")\n\n        // IPv4-compatible (deprecated but valid syntax)\n        let ipv4Compat = try IPv6Address.parse(\"::c000:0201\")\n        let expectedCompat: [UInt8] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc0, 0x00, 0x02, 0x01]\n        #expect(ipv4Compat.bytes == expectedCompat, \"::c000:0201 should parse correctly\")\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.2 - Invalid formats should fail\",\n        arguments: [\n            \"2001:db8\",  // Too few groups without ::\n            \"2001:db8:1:2:3:4:5:6:7\",  // Too many groups\n            \"2001:db8:1:2:3:4:5:6:7:8:9\",  // Too many groups\n            \"2001:db8:::1\",  // Triple colon\n            \"2001:db8::1::2\",  // Multiple ::\n            \"gggg::1\",  // Invalid hex character\n            \"2001:db8:xyz::1\",  // Invalid hex character\n            \"::ffff:\",  // Trailing colon\n            \":2001:db8::1\",  // Leading single colon\n            \"2001:db8::1:\",  // Trailing single colon\n        ]\n    )\n    func testRFC4291Section22InvalidFormatsShouldFail(invalid: String) {\n        #expect(throws: AddressError.self, \"Invalid format should fail: \\(invalid)\") {\n            _ = try IPv6Address.parse(invalid)\n        }\n    }\n\n    @Test(\"RFC 4291 Section 2.2 - Maximum values\")\n    func testRFC4291Section22MaximumValues() throws {\n        // All FFs - maximum value\n        let maxAddress = try IPv6Address.parse(\"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\")\n        #expect(maxAddress.bytes.allSatisfy { $0 == 0xFF }, \"All groups should be 0xFFFF\")\n\n        // Mix of max and min values\n        let mixedMax = try IPv6Address.parse(\"ffff:0:ffff:0:ffff:0:ffff:0\")\n        let expectedMixed: [UInt8] = [0xff, 0xff, 0, 0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0, 0]\n        #expect(mixedMax.bytes == expectedMixed, \"Should alternate between max and zero\")\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.2 - Single zero groups\",\n        arguments: [\n            (\"2001:db8:0:1:2:3:4:5\", \"2001:db8:0:1:2:3:4:5\"),  // Single 0, no compression needed\n            (\"2001:db8:0:0:1:2:3:4\", \"2001:db8::1:2:3:4\"),  // Two zeros can be compressed\n            (\"0:0:0:0:0:0:0:1\", \"::1\"),  // All zeros except last\n        ]\n    )\n    func testRFC4291Section22SingleZeroGroups(withZero: String, withCompression: String) throws {\n        let zeroBytes = try IPv6Address.parse(withZero).bytes\n        let compressedBytes = try IPv6Address.parse(withCompression).bytes\n        #expect(zeroBytes == compressedBytes, \"'\\(withZero)' should equal '\\(withCompression)'\")\n    }\n\n    @Test(\"RFC 4291 Section 2.2 - Boundary conditions\")\n    func testRFC4291Section22BoundaryConditions() throws {\n        // Single non-zero value in each position\n        for position in 0..<8 {\n            var groups = [String](repeating: \"0\", count: 8)\n            groups[position] = \"1\"\n            let address = groups.joined(separator: \":\")\n\n            #expect(throws: Never.self, \"Single non-zero at position \\(position) should parse\") {\n                _ = try IPv6Address.parse(address)\n            }\n        }\n\n        // Verify the bytes are correct\n        let firstPosition = try IPv6Address.parse(\"1:0:0:0:0:0:0:0\")\n        #expect(firstPosition.bytes[0] == 0 && firstPosition.bytes[1] == 1, \"First group should be 0x0001\")\n\n        let lastPosition = try IPv6Address.parse(\"0:0:0:0:0:0:0:1\")\n        #expect(lastPosition.bytes[14] == 0 && lastPosition.bytes[15] == 1, \"Last group should be 0x0001\")\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.2 - Hex digit limits\",\n        arguments: [\n            (\"1:2:3:4:5:6:7:8\", \"1-digit groups\"),\n            (\"12:34:56:78:9a:bc:de:f0\", \"2-digit groups\"),\n            (\"123:456:789:abc:def:123:456:789\", \"3-digit groups\"),\n            (\"1234:5678:9abc:def0:1234:5678:9abc:def0\", \"4-digit groups\"),\n            (\"1:12:123:1234:1:12:123:1234\", \"Mixed digit counts\"),\n        ]\n    )\n    func testRFC4291Section22HexDigitLimits(address: String, description: String) throws {\n        #expect(throws: Never.self, \"Should parse \\(description): \\(address)\") {\n            _ = try IPv6Address.parse(address)\n        }\n    }\n\n    @Test(\"RFC 4291 Section 2.2 - Equivalence of different representations\")\n    func testRFC4291Section22EquivalenceOfRepresentations() throws {\n        let equivalentGroups: [[String]] = [\n            // Same address, different representations\n            [\n                \"2001:0db8:0000:0000:0000:0000:0000:0001\",\n                \"2001:db8:0:0:0:0:0:1\",\n                \"2001:db8::1\",\n                \"2001:0DB8::1\",\n                \"2001:0DB8:0000:0000:0000:0000:0000:0001\",\n            ],\n            [\n                \"fe80:0000:0000:0000:0000:0000:0000:0001\",\n                \"fe80::1\",\n                \"FE80::1\",\n                \"fe80:0:0:0:0:0:0:1\",\n            ],\n            [\n                \"0000:0000:0000:0000:0000:0000:0000:0000\",\n                \"::\",\n                \"0:0:0:0:0:0:0:0\",\n            ],\n        ]\n\n        for group in equivalentGroups {\n            let bytesArray = try group.map { try IPv6Address.parse($0).bytes }\n            let firstBytes = bytesArray[0]\n\n            for (index, bytes) in bytesArray.enumerated() {\n                #expect(bytes == firstBytes, \"All forms should be equivalent: \\(group[index])\")\n            }\n        }\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.2 - Zero compression selection (longest run)\",\n        arguments: [\n            (\"2001:db8:0:0:1:0:0:1\", \"Two runs of 2 zeros each\"),\n            (\"2001:0:0:0:db8:0:0:1\", \"Run of 3 and run of 2\"),\n            (\"2001:db8:0:0:0:0:1:1\", \"Run of 4 zeros in middle\"),\n        ]\n    )\n    func testRFC4291Section22ZeroCompressionLongestRun(address: String, description: String) throws {\n        #expect(throws: Never.self, \"Should parse \\(description): \\(address)\") {\n            _ = try IPv6Address.parse(address)\n        }\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.2 - Edge case with :: at different positions\",\n        arguments: [\n            (\"::1\", \"0:0:0:0:0:0:0:1\"),\n            (\"1::\", \"1:0:0:0:0:0:0:0\"),\n            (\"1::1\", \"1:0:0:0:0:0:0:1\"),\n            (\"1:2::1\", \"1:2:0:0:0:0:0:1\"),\n            (\"1::2:3\", \"1:0:0:0:0:0:2:3\"),\n            (\"1:2:3::4:5:6\", \"1:2:3:0:0:4:5:6\"),\n        ]\n    )\n    func testRFC4291Section22DoubleColonAtDifferentPositions(compressed: String, expanded: String) throws {\n        let compressedBytes = try IPv6Address.parse(compressed).bytes\n        let expandedBytes = try IPv6Address.parse(expanded).bytes\n        #expect(compressedBytes == expandedBytes, \"'\\(compressed)' should equal '\\(expanded)'\")\n    }\n\n    // MARK: - RFC 4291 Section 2.2 IPv4 Mixed Notation Tests\n\n    @Test(\n        \"RFC 4291 Section 2.2 - IPv4 mixed notation basic formats\",\n        arguments: [\n            // RFC 4291 examples\n            (\"0:0:0:0:0:0:13.1.68.3\", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 1, 68, 3]),\n            (\"::13.1.68.3\", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 1, 68, 3]),\n\n            // IPv4-mapped IPv6 address (::ffff:x.x.x.x)\n            (\"::ffff:129.144.52.38\", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 129, 144, 52, 38]),\n            (\"0:0:0:0:0:ffff:129.144.52.38\", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 129, 144, 52, 38]),\n\n            // IPv4-compatible IPv6 address (deprecated but valid syntax)\n            (\"::192.168.1.1\", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 168, 1, 1]),\n            (\"::0.0.0.1\", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]),\n\n            // Various valid positions\n            (\"2001:db8::192.0.2.1\", [0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 2, 1]),\n            (\"fe80::192.168.1.1\", [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 168, 1, 1]),\n        ]\n    )\n    func testRFC4291Section22IPv4MixedNotation(address: String, expected: [UInt8]) throws {\n        let parsed = try IPv6Address.parse(address)\n        #expect(parsed.bytes == expected, \"IPv4 mixed notation '\\(address)' should parse correctly\")\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.2 - IPv4 mixed notation with full IPv6 prefix\",\n        arguments: [\n            (\"0:0:0:0:0:0:192.168.1.1\", [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 168, 1, 1]),\n            (\"2001:db8:0:0:0:0:192.0.2.1\", [0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 2, 1]),\n            (\"64:ff9b::192.0.2.33\", [0, 0x64, 0xff, 0x9b, 0, 0, 0, 0, 0, 0, 0, 0, 192, 0, 2, 33]),\n        ]\n    )\n    func testRFC4291Section22IPv4MixedNotationFullPrefix(address: String, expected: [UInt8]) throws {\n        let parsed = try IPv6Address.parse(address)\n        #expect(parsed.bytes == expected, \"Full prefix with IPv4 '\\(address)' should parse correctly\")\n    }\n\n    @Test(\"RFC 4291 Section 2.2 - IPv4 mixed notation edge cases\")\n    func testRFC4291Section22IPv4MixedNotationEdgeCases() throws {\n        // Maximum values\n        let maxIPv4 = try IPv6Address.parse(\"::255.255.255.255\")\n        let expectedMax: [UInt8] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255]\n        #expect(maxIPv4.bytes == expectedMax, \"Maximum IPv4 values should work\")\n\n        // Minimum values\n        let minIPv4 = try IPv6Address.parse(\"::0.0.0.0\")\n        let expectedMin: [UInt8] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n        #expect(minIPv4.bytes == expectedMin, \"Minimum IPv4 values should work\")\n\n        // With non-zero prefix\n        let withPrefix = try IPv6Address.parse(\"2001:db8:85a3::8a2e:255.255.255.255\")\n        let expectedPrefix: [UInt8] = [0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0, 0, 0, 0, 0x8a, 0x2e, 255, 255, 255, 255]\n        #expect(withPrefix.bytes == expectedPrefix, \"IPv4 with hex prefix should work\")\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.2 - IPv4 mixed notation invalid formats\",\n        arguments: [\n            \"::192.168.1\",  // Incomplete IPv4\n            \"::192.168.1.1.1\",  // Too many IPv4 octets\n            \"::256.1.1.1\",  // IPv4 octet out of range\n            \"::192.168.001.1\",  // Leading zeros in IPv4\n            \"::192.168.-1.1\",  // Negative in IPv4\n            \"::192.168.1.a\",  // Non-numeric in IPv4\n            \"2001:db8:1:2:3:4:5:192.168.1.1\",  // Too many hex groups before IPv4\n            \"::192.168.1.1:1234\",  // Extra hex after IPv4\n        ]\n    )\n    func testRFC4291Section22IPv4MixedNotationInvalid(invalid: String) {\n        #expect(throws: AddressError.self, \"Invalid IPv4 mixed notation should fail: \\(invalid)\") {\n            _ = try IPv6Address.parse(invalid)\n        }\n    }\n\n    @Test(\"RFC 4291 Section 2.2 - IPv4 mixed notation equivalence\")\n    func testRFC4291Section22IPv4MixedNotationEquivalence() throws {\n        // These should all represent the same address\n        let equivalentForms = [\n            \"0:0:0:0:0:0:192.0.2.1\",\n            \"::192.0.2.1\",\n            \"::c000:201\",  // Same as 192.0.2.1 in hex\n        ]\n\n        let bytesArray = try equivalentForms.map { try IPv6Address.parse($0).bytes }\n        let firstBytes = bytesArray[0]\n\n        for (index, bytes) in bytesArray.enumerated() {\n            #expect(bytes == firstBytes, \"All forms should be equivalent: \\(equivalentForms[index])\")\n        }\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.2 - IPv4-mapped IPv6 addresses\",\n        arguments: [\n            (\"127.0.0.1\", \"::ffff:127.0.0.1\"),\n            (\"192.168.1.1\", \"::ffff:192.168.1.1\"),\n            (\"8.8.8.8\", \"::ffff:8.8.8.8\"),\n            (\"0.0.0.0\", \"::ffff:0.0.0.0\"),\n            (\"255.255.255.255\", \"::ffff:255.255.255.255\"),\n        ]\n    )\n    func testRFC4291Section22IPv4MappedAddresses(ipv4: String, ipv6: String) throws {\n        let parsed = try IPv6Address.parse(ipv6)\n\n        // First 10 bytes should be 0\n        #expect(parsed.bytes[0..<10].allSatisfy { $0 == 0 }, \"First 10 bytes should be zero\")\n\n        // Next 2 bytes should be 0xff\n        #expect(parsed.bytes[10] == 0xff && parsed.bytes[11] == 0xff, \"Bytes 10-11 should be 0xffff\")\n\n        // Last 4 bytes should match IPv4 address\n        let ipv4Parsed = try IPv4Address.parse(ipv4)\n        let ipv4Bytes = [\n            UInt8((ipv4Parsed >> 24) & 0xFF),\n            UInt8((ipv4Parsed >> 16) & 0xFF),\n            UInt8((ipv4Parsed >> 8) & 0xFF),\n            UInt8(ipv4Parsed & 0xFF),\n        ]\n        #expect(Array(parsed.bytes[12..<16]) == ipv4Bytes, \"Last 4 bytes should match IPv4\")\n    }\n\n    @Test(\n        \"RFC 4291 Section 2.2 - IPv4 mixed notation with zone identifier\",\n        arguments: [\n            \"::ffff:192.168.1.1%eth0\",\n            \"fe80::192.168.1.1%lo0\",\n        ]\n    )\n    func testRFC4291Section22IPv4MixedNotationWithZone(testCase: String) throws {\n        #expect(throws: Never.self, \"IPv4 mixed notation with zone should parse: \\(testCase)\") {\n            let parsed = try IPv6Address.parse(testCase)\n            #expect(parsed.zone != nil, \"Zone should be preserved\")\n        }\n    }\n}\n\n// MARK: - Array Extension for Prefix Validation\n\nextension Array where Element == UInt8 {\n    /// Checks if this byte array has another array as a prefix up to the specified number of bits\n    /// - Parameters:\n    ///   - prefix: The potential prefix array\n    ///   - bits: Number of bits to compare (0-128 for IPv6)\n    /// - Returns: true if the prefix matches for the specified number of bits\n    func hasPrefix(_ prefix: [UInt8], upToBits bits: Int) -> Bool {\n        guard self.count >= 16 && prefix.count >= 16 else { return false }\n        guard bits >= 0 && bits <= 128 else { return false }\n\n        let fullBytes = bits / 8\n        let remainingBits = bits % 8\n\n        // Compare full bytes\n        for i in 0..<fullBytes {\n            if self[i] != prefix[i] {\n                return false\n            }\n        }\n\n        // Compare partial byte if needed\n        if remainingBits > 0 && fullBytes < 16 {\n            let shiftAmount = 8 - remainingBits\n            let mask: UInt8 = shiftAmount >= 8 ? 0 : (0xFF << shiftAmount)\n            return (self[fullBytes] & mask) == (prefix[fullBytes] & mask)\n        }\n\n        return true\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationExtrasTests/TestIPv6Address.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationExtras\n\n@Suite(\"IPv6 Address Tests\")\nstruct IPv6AddressTests {\n\n    // MARK: - String Representation Tests (RFC 5952)\n\n    @Test(\n        \"IPv6 address string representation - RFC 5952\",\n        arguments: [\n            // Zero compression algorithm tests\n            (\"0:0:0:0:0:0:0:0\", \"::\", \"all zeros - unspecified address\"),\n            (\"0:0:0:0:0:0:0:1\", \"::1\", \"leading zeros - loopback\"),\n            (\"2001:0db8:0:0:0:0:0:0\", \"2001:db8::\", \"trailing zeros\"),\n            (\"2001:0:0:0:0:0:0db8:1\", \"2001::db8:1\", \"middle zeros - longest run\"),\n            (\"2001:0:0:0:0:0db8:0:1\", \"2001::db8:0:1\", \"multiple runs - prefer longest\"),\n            (\"2001:0:0db8:0:1:0:0:2\", \"2001:0:db8:0:1::2\", \"tie-breaking - first occurrence wins\"),\n            (\"2001:0:0db8:1:2:3:4:5\", \"2001:0:db8:1:2:3:4:5\", \"single zero - no compression (min 2 required)\"),\n            (\"2001:0db8:0:0:1:2:3:4\", \"2001:db8::1:2:3:4\", \"exactly 2 zeros - minimum for compression\"),\n            (\"0:0:0:0:1234:0:0:0\", \"::1234:0:0:0\", \"tie-breaking - first run wins (4 vs 3 zeros)\"),\n\n            // RFC 5952 formatting rules\n            (\n                \"ABCD:EF01:2345:6789:9ABC:DEF0:1122:3344\", \"abcd:ef01:2345:6789:9abc:def0:1122:3344\",\n                \"lowercase hex (Section 4.3)\"\n            ),\n            (\"0001:0002:0003:0004:0005:0006:0007:0008\", \"1:2:3:4:5:6:7:8\", \"no leading zeros (Section 4.1)\"),\n\n            // Edge cases\n            (\"2001:a:a:a:0:0db8:0:1\", \"2001:a:a:a:0:db8:0:1\", \"only single zeros scattered - no compression\"),\n        ]\n    )\n    func testIPv6StringRepresentation(input: String, expected: String, description: String) throws {\n        let addr = try IPv6Address.parse(input)\n        #expect(\n            addr.description == expected,\n            \"Expected '\\(expected)' but got '\\(addr.description)' for input: '\\(input)' (\\(description))\"\n        )\n    }\n\n    // MARK: - isUnspecified Tests\n\n    @Test(\n        \"isUnspecified - RFC 4291 Section 2.5.2\",\n        arguments: [\n            (\"::\", true, \"unspecified address (short form)\"),\n            (\"0:0:0:0:0:0:0:0\", true, \"unspecified address (full form)\"),\n            (IPv6Address.unspecified.description, true, \"unspecified\"),\n            (\"::1\", false, \"loopback\"),\n            (\"0:0:0:0:0:0:0:1\", false, \"loopback (full form)\"),\n            (\"fe80::1\", false, \"link-local\"),\n            (\"2001:db8::1\", false, \"global unicast\"),\n        ]\n    )\n    func testIsUnspecified(address: String, expected: Bool, description: String) throws {\n        let addr = try IPv6Address.parse(address)\n        #expect(\n            addr.isUnspecified == expected,\n            \"Address \\(address) (\\(description)) should\\(expected ? \"\" : \" not\") be unspecified\"\n        )\n    }\n\n    // MARK: - isLoopback Tests\n\n    @Test(\n        \"isLoopback - RFC 4291 Section 2.5.3\",\n        arguments: [\n            (\"::1\", true, \"loopback (short form)\"),\n            (\"0:0:0:0:0:0:0:1\", true, \"loopback (full form)\"),\n            (IPv6Address.loopback.description, true, \"loopback var\"),\n            (\"::\", false, \"unspecified\"),\n            (\"::2\", false, \"not loopback\"),\n            (\"0:0:0:0:0:0:0:2\", false, \"not loopback (full form)\"),\n            (\"fe80::1\", false, \"link-local\"),\n            (\"2001:db8::1\", false, \"global unicast\"),\n        ]\n    )\n    func testIsLoopback(addressString: String, expected: Bool, description: String) throws {\n        let addr = try IPv6Address.parse(addressString)\n        #expect(\n            addr.isLoopback == expected,\n            \"Address \\(addressString) (\\(description)) should\\(expected ? \"\" : \" not\") be loopback\"\n        )\n    }\n\n    // MARK: - isMulticast Tests\n\n    @Test(\n        \"isMulticast - RFC 4291 Section 2.7\",\n        arguments: [\n            // Positive cases - all multicast addresses start with ff\n            (\"ff00::1\", true, \"Reserved multicast\"),\n            (\"ff01::1\", true, \"Interface-local multicast\"),\n            (\"ff02::1\", true, \"Link-local multicast (all nodes)\"),\n            (\"ff02::2\", true, \"Link-local multicast (all routers)\"),\n            (\"ff05::1\", true, \"Site-local multicast\"),\n            (\"ff0e::1\", true, \"Global multicast\"),\n            (\"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\", true, \"Max multicast\"),\n            // Negative cases\n            (\"::\", false, \"unspecified\"),\n            (\"::1\", false, \"loopback\"),\n            (\"fe80::1\", false, \"link-local unicast\"),\n            (\"2001:db8::1\", false, \"global unicast\"),\n            (\"fd00::1\", false, \"unique local\"),\n        ]\n    )\n    func testIsMulticast(addressString: String, expected: Bool, description: String) throws {\n        let addr = try IPv6Address.parse(addressString)\n        #expect(\n            addr.isMulticast == expected,\n            \"Address \\(addressString) (\\(description)) should\\(expected ? \"\" : \" not\") be multicast\"\n        )\n    }\n\n    // MARK: - isLinkLocal Tests\n\n    @Test(\n        \"isLinkLocal - RFC 4291 Section 2.5.6\",\n        arguments: [\n            // Positive cases - fe80::/10\n            (\"fe80::1\", true, \"basic link-local\"),\n            (\"fe80::dead:beef\", true, \"link-local with hex\"),\n            (\"fe80:0:0:0:0:0:0:1\", true, \"link-local (full form)\"),\n            (\"fe80::1234:5678:90ab:cdef\", true, \"link-local with interface ID\"),\n            (\"febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff\", true, \"Last address in fe80::/10\"),\n            // Negative cases\n            (\"::\", false, \"unspecified\"),\n            (\"::1\", false, \"loopback\"),\n            (\"fec0::1\", false, \"site-local (deprecated)\"),\n            (\"ff02::1\", false, \"multicast\"),\n            (\"2001:db8::1\", false, \"global unicast\"),\n            (\"fd00::1\", false, \"unique local\"),\n        ]\n    )\n    func testIsLinkLocal(addressString: String, expected: Bool, description: String) throws {\n        let addr = try IPv6Address.parse(addressString)\n        #expect(\n            addr.isLinkLocal == expected,\n            \"Address \\(addressString) (\\(description)) should\\(expected ? \"\" : \" not\") be link-local\"\n        )\n    }\n\n    // MARK: - isUniqueLocal Tests\n\n    @Test(\n        \"isUniqueLocal - RFC 4193\",\n        arguments: [\n            // Positive cases - fc00::/7 (fc00::/8 and fd00::/8)\n            (\"fc00::1\", true, \"fc00 unique local\"),\n            (\"fc00:dead:beef::1\", true, \"fc00 with hex\"),\n            (\"fd00::1\", true, \"fd00 unique local\"),\n            (\"fd12:3456:789a::1\", true, \"fd00 with prefix\"),\n            (\"fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\", true, \"max unique local\"),\n            // Negative cases\n            (\"::\", false, \"unspecified\"),\n            (\"::1\", false, \"loopback\"),\n            (\"fe80::1\", false, \"link-local\"),\n            (\"ff02::1\", false, \"multicast\"),\n            (\"2001:db8::1\", false, \"global unicast\"),\n        ]\n    )\n    func testIsUniqueLocal(addressString: String, expected: Bool, description: String) throws {\n        let addr = try IPv6Address.parse(addressString)\n        #expect(\n            addr.isUniqueLocal == expected,\n            \"Address \\(addressString) (\\(description)) should\\(expected ? \"\" : \" not\") be unique local\"\n        )\n    }\n\n    // MARK: - isGlobalUnicast Tests\n\n    @Test(\n        \"isGlobalUnicast - RFC 4291 Section 2.5.4\",\n        arguments: [\n            // Positive cases - routable on the global internet\n            (\"2001:db8::1\", true, \"Documentation (but still global unicast format)\"),\n            (\"2001:4860:4860::8888\", true, \"Google DNS\"),\n            (\"2606:4700:4700::1111\", true, \"Cloudflare DNS\"),\n            (\"2001:500::1\", true, \"Root DNS server\"),\n            (\"2a00:1450:4001::1\", true, \"Google\"),\n            // Negative cases - special addresses\n            (\"::\", false, \"unspecified\"),\n            (\"::1\", false, \"loopback\"),\n            (\"fe80::1\", false, \"link-local\"),\n            (\"ff02::1\", false, \"multicast\"),\n            (\"fc00::1\", false, \"unique local\"),\n            (\"fd00::1\", false, \"unique local\"),\n        ]\n    )\n    func testIsGlobalUnicast(addressString: String, expected: Bool, description: String) throws {\n        let addr = try IPv6Address.parse(addressString)\n        #expect(\n            addr.isGlobalUnicast == expected,\n            \"Address \\(addressString) (\\(description)) should\\(expected ? \"\" : \" not\") be global unicast\"\n        )\n    }\n\n    // MARK: - isDocumentation Tests\n\n    @Test(\n        \"isDocumentation - RFC 3849\",\n        arguments: [\n            // Positive cases - 2001:db8::/32\n            (\"2001:db8::1\", true, \"basic documentation address\"),\n            (\"2001:db8::\", true, \"documentation prefix\"),\n            (\"2001:db8:0:0:0:0:0:1\", true, \"documentation (full form)\"),\n            (\"2001:db8:1234:5678:90ab:cdef:1234:5678\", true, \"documentation with all fields\"),\n            (\"2001:db8:ffff:ffff:ffff:ffff:ffff:ffff\", true, \"max documentation address\"),\n            // Negative cases\n            (\"2001:db7::1\", false, \"Just before documentation range\"),\n            (\"2001:db9::1\", false, \"Just after documentation range\"),\n            (\"2001:4860:4860::8888\", false, \"Google DNS\"),\n            (\"::\", false, \"unspecified\"),\n            (\"::1\", false, \"loopback\"),\n        ]\n    )\n    func testIsDocumentation(addressString: String, expected: Bool, description: String) throws {\n        let addr = try IPv6Address.parse(addressString)\n        #expect(\n            addr.isDocumentation == expected,\n            \"Address \\(addressString) (\\(description)) should\\(expected ? \"\" : \" not\") be documentation\"\n        )\n    }\n\n    @Test(\n        \"Codable encodes to string representation\",\n        arguments: [\n            (\"::1\", \"::1\"),\n            (\"2001:db8::1\", \"2001:db8::1\"),\n            (\"::\", \"::\"),\n            (\"fe80::1\", \"fe80::1\"),\n        ]\n    )\n    func testCodableEncode(input: String, expected: String) throws {\n        let original = try IPv6Address(input)\n        let encoded = try JSONEncoder().encode(original)\n        #expect(String(data: encoded, encoding: .utf8) == \"\\\"\\(expected)\\\"\")\n    }\n\n    @Test(\n        \"Codable decodes from string representation\",\n        arguments: [\n            \"::1\",\n            \"2001:db8::1\",\n            \"::\",\n            \"fe80::1\",\n        ]\n    )\n    func testCodableDecode(address: String) throws {\n        let json = Data(\"\\\"\\(address)\\\"\".utf8)\n        let decoded = try JSONDecoder().decode(IPv6Address.self, from: json)\n        let expected = try IPv6Address(address)\n        #expect(decoded == expected)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationExtrasTests/TestIPv6IPv4Parsing.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationExtras\n\n@Suite(\"IPv6 Mixed IPv4 Notation Tests\")\nstruct IPv6IPv4ParsingTests {\n\n    @Test(\n        \"Extract IPv4 suffix from various IPv6 formats\",\n        arguments: [\n            (\"::192.168.1.1\", \"::\", [UInt8(192), UInt8(168), UInt8(1), UInt8(1)]),\n            (\"::ffff:192.0.2.1\", \"::ffff\", [UInt8(192), UInt8(0), UInt8(2), UInt8(1)]),\n            (\"fe80::192.168.1.1\", \"fe80::\", [UInt8(192), UInt8(168), UInt8(1), UInt8(1)]),\n            (\"2001:db8::192.0.2.1\", \"2001:db8::\", [UInt8(192), UInt8(0), UInt8(2), UInt8(1)]),\n            (\"0:0:0:0:0:0:192.168.1.1\", \"0:0:0:0:0:0\", [UInt8(192), UInt8(168), UInt8(1), UInt8(1)]),\n        ]\n    )\n    func testIPv4SuffixExtraction(input: String, expectedIPv6: String, expectedIPv4: [UInt8]) throws {\n        let result = try #require(try IPv6Address.extractIPv4Suffix(from: input))\n        #expect(result.0 == expectedIPv6)\n        #expect(result.1 == expectedIPv4)\n    }\n\n    @Test(\n        \"No IPv4 suffix for pure IPv6 addresses\",\n        arguments: [\n            \"2001:db8::1\",\n            \"fe80::1\",\n            \"::\",\n            \"::1\",\n        ]\n    )\n    func testPureIPv6ReturnsNil(address: String) throws {\n        #expect(try IPv6Address.extractIPv4Suffix(from: address) == nil)\n    }\n\n    @Test(\n        \"Invalid IPv4 suffix throws error\",\n        arguments: [\n            \"::256.1.1.1\",\n            \"::192.168.1\",\n            \"::192.168.001.1\",\n        ]\n    )\n    func testInvalidIPv4Throws(invalid: String) {\n        #expect(throws: AddressError.self) {\n            _ = try IPv6Address.extractIPv4Suffix(from: invalid)\n        }\n    }\n\n    @Test(\n        \"IPv4 bytes always at positions 12-15\",\n        arguments: [\n            \"::192.168.1.1\",\n            \"::ffff:127.0.0.1\",\n            \"fe80::10.0.0.1\",\n        ]\n    )\n    func testIPv4BytePlacement(address: String) throws {\n        let parsed = try IPv6Address.parse(address)\n        let ipv4String = String(address.split(separator: \":\").last!)\n        let ipv4 = try IPv4Address.parse(ipv4String)\n\n        #expect(parsed.bytes[12] == UInt8((ipv4 >> 24) & 0xFF))\n        #expect(parsed.bytes[13] == UInt8((ipv4 >> 16) & 0xFF))\n        #expect(parsed.bytes[14] == UInt8((ipv4 >> 8) & 0xFF))\n        #expect(parsed.bytes[15] == UInt8(ipv4 & 0xFF))\n    }\n\n    @Test(\n        \"Unspecified address with IPv4 suffix\",\n        arguments: [\n            (\"::192.168.1.1\", [UInt8(192), UInt8(168), UInt8(1), UInt8(1)]),\n            (\"::0.0.0.1\", [UInt8(0), UInt8(0), UInt8(0), UInt8(1)]),\n            (\"::255.255.255.255\", [UInt8(255), UInt8(255), UInt8(255), UInt8(255)]),\n        ]\n    )\n    func testUnspecifiedWithIPv4(address: String, ipv4: [UInt8]) throws {\n        let parsed = try IPv6Address.parse(address)\n        #expect(parsed.bytes[0..<12].allSatisfy { $0 == 0 })\n        #expect(Array(parsed.bytes[12..<16]) == ipv4)\n    }\n\n    @Test(\"IPv4 with zone identifier\")\n    func testIPv4WithZone() throws {\n        let parsed = try IPv6Address.parse(\"::192.168.1.1%lo0\")\n\n        #expect(parsed.zone == \"lo0\")\n        #expect(Array(parsed.bytes[12..<16]) == [192, 168, 1, 1])\n    }\n\n    @Test(\n        \"IPv4-mapped addresses (::ffff:x.x.x.x)\",\n        arguments: [\n            \"::ffff:127.0.0.1\",\n            \"::ffff:192.168.1.1\",\n        ]\n    )\n    func testIPv4MappedAddresses(address: String) throws {\n        let parsed = try IPv6Address.parse(address)\n\n        #expect(parsed.bytes[0..<10].allSatisfy { $0 == 0 })\n        #expect(parsed.bytes[10] == 0xff && parsed.bytes[11] == 0xff)\n    }\n\n    @Test(\"Complex ellipsis with IPv4\")\n    func testComplexEllipsisWithIPv4() throws {\n        let address = \"2001:db8:85a3::8a2e:192.168.1.1\"\n        let parsed = try IPv6Address.parse(address)\n\n        #expect(parsed.bytes[10] == 0x8a && parsed.bytes[11] == 0x2e)\n        #expect(Array(parsed.bytes[12..<16]) == [192, 168, 1, 1])\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationExtrasTests/TestMACAddress.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationExtras\n\n@Suite(\"MACAddress Tests\")\nstruct MACAddressTests {\n\n    // MARK: - Initializer Tests\n\n    @Suite(\"Initializers\")\n    struct InitializerTests {\n\n        @Test(\n            \"UInt64 initializer - valid addresses\",\n            arguments: [\n                //(0x0123_4567_89ab, \"01:23:45:67:89:ab\"),  // a valid address\n                //(0x0000_0000_0000, \"00:00:00:00:00:00\"),  // zero address\n                //(0xFFFF_FFFF_FFFF, \"ff:ff:ff:ff:ff:ff\"),  // max address\n                (0xffff_0123_4567_89ab, \"01:23:45:67:89:ab\")  // drops the most significant 16 bits\n            ]\n        )\n        func testUInt64InitializerValid(inputValue: UInt64, description: String) {\n            let address = MACAddress(inputValue)\n            #expect(address.value == inputValue & 0x0000_ffff_ffff_ffff)\n        }\n\n        @Test(\n            \"String initializer - valid addresses\",\n            arguments: [\n                (\"01:23:45:67:89:ab\", 0x0123_4567_89ab),  // colon separators\n                (\"01-23-45-67-89-ab\", 0x0123_4567_89ab),  // dash separators\n                (\"ab:cd:ef:AB:CD:EF\", 0xabcd_efab_cdef),  // mixed case\n                (\"00:00:00:00:00:00\", 0x0000_0000_0000),  // zero address\n                (\"ff:ff:ff:ff:ff:ff\", 0xffff_ffff_ffff),  // max address\n            ]\n        )\n        func testStringInitializerValid(addressString: String, expectedValue: UInt64) throws {\n            let address = try MACAddress(addressString)\n            #expect(address.value == expectedValue)\n        }\n\n        @Test(\n            \"String initializer - invalid addresses\",\n            arguments: [\n                \"\",  // empty string\n                \"01:23:45:67:89\",  // too few octets\n                \"01:23:45:67:89:ab:cd\",  // too many octets\n                \"01:23:45:67:89:\",  // empty octet\n                \":23:45:67:89:ab\",  // empty octet\n                \"01::45:67:89:ab\",  // empty octet\n                \"01:23:45:67:89:a\",  // short octet\n                \"1:23:45:67:89:ab\",  // short octet\n                \"01:2:45:67:89:ab\",  // short octet\n                \"01:23:45:67:89:abc\",  // long octet\n                \"012:23:45:67:89:ab\",  // long octet\n                \"01:234:45:67:89:ab\",  // long octet\n                \"01:23:45:67:89:@G\",  // invalid content 0x40, 0x47\n                \"`g:23:45:67:89:ab\",  // invalid content 0x60, 0x67\n                \"01:hi:45:67:89:ab\",  // invalid content\n                \" 01:23:45:67:89:ab\",  // leading whitespace\n                \"01:23:45:67:89:ab \",  // trailing whitespace\n                \"01: 23:45:67:89:ab\",  // internal whitespace\n            ]\n        )\n        func testStringInitializerInvalid(invalidAddress: String) {\n            #expect(throws: AddressError.self) {\n                try MACAddress(invalidAddress)\n            }\n        }\n    }\n\n    // MARK: - Property Tests\n\n    @Suite(\"Properties\")\n    struct PropertyTests {\n\n        @Test(\n            \"bytes property\",\n            arguments: [\n                (\n                    UInt64(0x0123_4567_89ab),\n                    [UInt8(0x01), UInt8(0x23), UInt8(0x45), UInt8(0x67), UInt8(0x89), UInt8(0xab)]\n                ),\n                (\n                    UInt64(0x0000_0000_0000),\n                    [UInt8(0x00), UInt8(0x00), UInt8(0x00), UInt8(0x00), UInt8(0x00), UInt8(0x00)]\n                ),\n                (\n                    UInt64(0xffff_ffff_ffff),\n                    [UInt8(0xff), UInt8(0xff), UInt8(0xff), UInt8(0xff), UInt8(0xff), UInt8(0xff)]\n                ),\n                (\n                    UInt64(0xffff_0123_4567_89ab),\n                    [UInt8(0x01), UInt8(0x23), UInt8(0x45), UInt8(0x67), UInt8(0x89), UInt8(0xab)]\n                ),\n            ]\n        )\n        func testBytesProperty(inputValue: UInt64, expectedBytes: [UInt8]) {\n            let address = MACAddress(inputValue)\n            #expect(address.bytes == expectedBytes)\n        }\n\n        @Test(\n            \"description property\",\n            arguments: [\n                (0x0123_4567_89ab, \"01:23:45:67:89:ab\"),\n                (0x0000_0000_0000, \"00:00:00:00:00:00\"),\n                (0xffff_ffff_ffff, \"ff:ff:ff:ff:ff:ff\"),\n                (0xffff_0123_4567_89ab, \"01:23:45:67:89:ab\"),\n            ]\n        )\n        func testDescriptionProperty(inputValue: UInt64, expectedDescription: String) {\n            let address = MACAddress(inputValue)\n            #expect(address.description == expectedDescription)\n        }\n\n        @Test(\n            \"isLocallyAdministered property\",\n            arguments: [\n                (0x0000_1234_5678, false),\n                (0x0200_1234_5678, true),\n            ]\n        )\n        func testIsLocallyAdministeredProperty(inputValue: UInt64, expectedValue: Bool) {\n            let address = MACAddress(inputValue)\n            #expect(address.isLocallyAdministered == expectedValue)\n        }\n\n        @Test(\n            \"isMulticast property\",\n            arguments: [\n                (0x0000_1234_5678, false),\n                (0x0100_1234_5678, true),\n            ]\n        )\n        func testIsMulticastProperty(inputValue: UInt64, expectedValue: Bool) {\n            let address = MACAddress(inputValue)\n            #expect(address.isMulticast == expectedValue)\n        }\n\n        @Test(\n            \"round-trip string conversion\",\n            arguments: [\n                \"01:23:45:67:89:ab\",\n                \"00:00:00:00:00:00\",\n                \"ff:ff:ff:ff:ff:ff\",\n                \"01-23-45-67-89-AB\",\n            ]\n        )\n        func testRoundTripStringConversion(addressString: String) throws {\n            let address = try MACAddress(addressString)\n            #expect(address.description == addressString.lowercased().replacingOccurrences(of: \"-\", with: \":\"))\n        }\n    }\n\n    // MARK: - Link Local Address Tests\n\n    @Suite(\"Link Local Addresses\")\n    struct LinkLocalAddressTests {\n\n        @Test(\n            \"Link local address\",\n            arguments: [\n                (0x39a7_9407_cbd0, 0xfd97_7b15_d62e_75ac_3ba7_94ff_fe07_cbd0),\n                (0x5e3b_68d7_e510, 0xfd97_7b15_d62e_75ac_5c3b_68ff_fed7_e510),\n            ]\n        )\n        func testLinkLocalAddress(mac: UInt64, ipv6: UInt128) throws {\n            let mac = MACAddress(mac)\n            let ipv6Prefix = IPv6Address(ipv6 & 0xffff_ffff_ffff_ffff_0000_0000_0000_0000)\n            let ipv6Address = try mac.ipv6Address(network: ipv6Prefix)\n            #expect(ipv6Address == IPv6Address(ipv6))\n        }\n    }\n\n    // MARK: - Protocol Conformance Tests\n\n    @Suite(\"Protocol Conformances\")\n    struct ProtocolConformanceTests {\n\n        @Test(\"Equatable conformance\")\n        func testEquatableConformance() {\n            let addr1 = MACAddress(0x0123_4567_89ab)\n            let addr2 = MACAddress(0x0123_4567_89ab)\n            let addr3 = MACAddress(0x0123_4567_89ac)\n\n            #expect(addr1 == addr2)\n            #expect(addr1 != addr3)\n            #expect(addr2 != addr3)\n        }\n\n        @Test(\"Hashable conformance\")\n        func testHashableConformance() {\n            let addr1 = MACAddress(0x0123_4567_89ab)\n            let addr2 = MACAddress(0x0123_4567_89ab)\n            let addr3 = MACAddress(0x0123_4567_89ac)\n\n            // Equal objects should have equal hash values\n            #expect(addr1.hashValue == addr2.hashValue)\n\n            // Different objects should ideally have different hash values\n            // (though this is not guaranteed, it's very likely for these values)\n            #expect(addr1.hashValue != addr3.hashValue)\n\n            // Test that addresses can be used in Sets and Dictionaries\n            let addressSet: Set<MACAddress> = [addr1, addr2, addr3]\n            #expect(addressSet.count == 2)  // addr1 and addr2 are equal\n\n            let addressDict = [addr1: \"localhost\", addr3: \"private\"]\n            #expect(addressDict[addr2] == \"localhost\")  // addr2 equals addr1\n        }\n\n        @Test(\"CustomStringConvertible conformance\")\n        func testCustomStringConvertibleConformance() {\n            let address = MACAddress(0x0123_4567_89ab)\n            let stringRepresentation = String(describing: address)\n            #expect(stringRepresentation == \"01:23:45:67:89:ab\")\n        }\n\n        @Test(\"Sendable conformance\")\n        func testSendableConformance() {\n            // This test verifies that MACAddress can be safely passed across concurrency boundaries\n            let address = MACAddress(0x0123_4567_89ab)\n\n            Task {\n                let taskAddress = address\n                #expect(taskAddress.value == 0x0123_4567_89ab)\n            }\n        }\n    }\n\n    // MARK: - Performance Tests\n\n    @Suite(\"Performance\")\n    struct PerformanceTests {\n\n        @Test(\"parsing performance\")\n        func testParsingPerformance() throws {\n            let testAddresses = [\n                \"01:23:45:67:89:ab\",\n                \"01-23-45-67-89-ab\",\n                \"01-23-45-67-89-a\",\n                \"01-23-45-67-89-abc\",\n            ]\n\n            // Warm up\n            for _ in 0..<100 {\n                for address in testAddresses {\n                    _ = try? MACAddress(address)\n                }\n            }\n\n            // Measure performance\n            let iterations = 10000\n            let startTime = Date()\n\n            for _ in 0..<iterations {\n                for address in testAddresses {\n                    _ = try? MACAddress(address)\n                }\n            }\n\n            let endTime = Date()\n            let totalTime = endTime.timeIntervalSince(startTime)\n            let averageTime = totalTime / Double(iterations * testAddresses.count)\n\n            // Should be very fast - less than 1ms per parse on average\n            #expect(averageTime < 0.001, \"Parsing should be fast: \\(averageTime)s per address\")\n        }\n\n        @Test(\"bytes property performance\")\n        func testBytesPropertyPerformance() {\n            let address = MACAddress(0x0123_4567_89ab)\n\n            let iterations = 100000\n            let startTime = Date()\n\n            for _ in 0..<iterations {\n                _ = address.bytes\n            }\n\n            let endTime = Date()\n            let totalTime = endTime.timeIntervalSince(startTime)\n            let averageTime = totalTime / Double(iterations)\n\n            // Should be very fast - less than 0.1ms per call on average\n            #expect(averageTime < 0.0001, \"Bytes property should be fast: \\(averageTime)s per call\")\n        }\n\n        @Test(\"description property performance\")\n        func testDescriptionPropertyPerformance() {\n            let address = MACAddress(0x0123_4567_89ab)\n\n            let iterations = 10000\n            let startTime = Date()\n\n            for _ in 0..<iterations {\n                _ = address.description\n            }\n\n            let endTime = Date()\n            let totalTime = endTime.timeIntervalSince(startTime)\n            let averageTime = totalTime / Double(iterations)\n\n            // Should be reasonably fast - less than 1ms per call on average\n            #expect(averageTime < 0.001, \"Description property should be fast: \\(averageTime)s per call\")\n        }\n    }\n\n    // MARK: - Integration Tests\n\n    @Suite(\"Integration\")\n    struct IntegrationTests {\n\n        @Test(\n            \"comprehensive round-trip test\",\n            arguments: [\n                (0x0123_4567_89ab, \"01:23:45:67:89:ab\"),\n                (0x0000_0000_0000, \"00:00:00:00:00:00\"),\n                (0xffff_ffff_ffff, \"ff:ff:ff:ff:ff:ff\"),\n            ]\n        )\n        func testComprehensiveRoundTrip(expectedValue: UInt64, expectedString: String) throws {\n            // Test UInt32 -> String\n            let addressFromUInt32 = MACAddress(expectedValue)\n            #expect(addressFromUInt32.description == expectedString)\n\n            // Test String -> UInt32\n            let addressFromString = try MACAddress(expectedString)\n            #expect(addressFromString.value == expectedValue)\n\n            // Test equality\n            #expect(addressFromUInt32 == addressFromString)\n        }\n\n        @Test(\n            \"error message consistency\",\n            arguments: [\n                \"\",\n                \"hi:00:00:00:00:00\",\n                \"01:23:45:67:89\",\n                \"01:23:45:67:89:ab:cd\",\n                \"001:23:45:67:89:ab:cd\",\n                \" 01:23:45:67:89:ab:cd\",\n                \"01:23:45:67:89:ab:cd \",\n            ]\n        )\n        func testErrorMessageConsistency(invalidInput: String) {\n            do {\n                _ = try MACAddress(invalidInput)\n                #expect(Bool(false), \"Should have thrown for input: \\(invalidInput)\")\n            } catch let error as AddressError {\n                #expect(error == AddressError.unableToParse)\n            } catch {\n                #expect(Bool(false), \"Should have thrown AddressError, got: \\(error)\")\n            }\n        }\n\n        @Test(\n            \"Codable encodes to string representation\",\n            arguments: [\n                \"01:23:45:67:89:ab\",\n                \"00:00:00:00:00:00\",\n                \"ff:ff:ff:ff:ff:ff\",\n            ]\n        )\n        func testCodableEncode(address: String) throws {\n            let original = try MACAddress(address)\n            let encoded = try JSONEncoder().encode(original)\n            #expect(String(data: encoded, encoding: .utf8) == \"\\\"\\(address)\\\"\")\n        }\n\n        @Test(\n            \"Codable decodes from string representation\",\n            arguments: [\n                \"01:23:45:67:89:ab\",\n                \"00:00:00:00:00:00\",\n                \"ff:ff:ff:ff:ff:ff\",\n            ]\n        )\n        func testCodableDecode(address: String) throws {\n            let json = Data(\"\\\"\\(address)\\\"\".utf8)\n            let decoded = try JSONDecoder().decode(MACAddress.self, from: json)\n            let expected = try MACAddress(address)\n            #expect(decoded == expected)\n        }\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationExtrasTests/TestNetworkAddress+Allocator.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//\n\nimport ContainerizationExtras\nimport Testing\n\n@testable import ContainerizationExtras\n\nfinal class TestAddressAllocators {\n    @Test\n    func testIPv4AddressAllocatorZeroSize() throws {\n        _ = try IPv4Address.allocator(lower: 0xffff_ffff, size: 1)\n        do {\n            _ = try IPv4Address.allocator(lower: 0xffff_ffff, size: 0)\n            #expect(Bool(false), \"Expected AllocatorError.rangeExceeded to be thrown\")\n        } catch {\n            #expect(error as? AllocatorError == .rangeExceeded, \"Unexpected error thrown: \\(error)\")\n        }\n    }\n\n    @Test\n    func testIPv4AddressAllocatorOverflow() throws {\n        _ = try IPv4Address.allocator(lower: 0xffff_ff00, size: 256)\n        do {\n            _ = try IPv4Address.allocator(lower: 0xffff_ff00, size: 257)\n            #expect(Bool(false), \"Expected AllocatorError.rangeExceeded to be thrown\")\n        } catch {\n            #expect(error as? AllocatorError == .rangeExceeded, \"Unexpected error thrown: \\(error)\")\n        }\n    }\n\n    @Test\n    func testUInt16AllocatorOverflow() throws {\n        _ = try UInt16.allocator(lower: 0xfff0, size: 16)\n        do {\n            _ = try UInt16.allocator(lower: 0xfff0, size: 17)\n            #expect(Bool(false), \"Expected AllocatorError.rangeExceeded to be thrown\")\n        } catch {\n            #expect(error as? AllocatorError == .rangeExceeded, \"Unexpected error thrown: \\(error)\")\n        }\n    }\n\n    @Test\n    func testUInt32AllocatorOverflow() throws {\n        _ = try UInt32.allocator(lower: 0xffff_fff0, size: 16)\n        do {\n            _ = try UInt32.allocator(lower: 0xffff_fff0, size: 17)\n            #expect(Bool(false), \"Expected AllocatorError.rangeExceeded to be thrown\")\n        } catch {\n            #expect(error as? AllocatorError == .rangeExceeded, \"Unexpected error thrown: \\(error)\")\n        }\n    }\n\n    @Test\n    func testFreeUnallocated() throws {\n        let allocator = try IPv4Address.allocator(\n            lower: 0xc0a8_4000, size: 256)\n        do {\n            _ = try allocator.release(IPv4Address(\"192.168.64.2\"))\n            #expect(Bool(false), \"Expected AllocatorError.notAllocated to be thrown\")\n        } catch {\n            #expect(error as? AllocatorError == .notAllocated(\"192.168.64.2\"), \"Unexpected error thrown: \\(error)\")\n        }\n    }\n\n    @Test\n    func testChoose() throws {\n        let allocator = try IPv4Address.allocator(\n            lower: 0xc0a8_4000, size: 2)\n        try allocator.reserve(IPv4Address(\"192.168.64.1\"))\n        do {\n            _ = try allocator.reserve(IPv4Address(\"192.168.64.1\"))\n            #expect(Bool(false), \"Expected AllocatorError.alreadyAllocated to be thrown\")\n        } catch {\n            #expect(error as? AllocatorError == .alreadyAllocated(\"192.168.64.1\"), \"Unexpected error thrown: \\(error)\")\n        }\n    }\n\n    @Test\n    func testipv4AddressAllocator() throws {\n        var allocations = Set<UInt32>()\n        let lower = try IPv4Address(\"192.168.64.1\").value & Prefix(length: 24)!.prefixMask32\n        let allocator = try IPv4Address.allocator(\n            lower: lower, size: 3)\n        allocations.insert(try allocator.allocate().value)\n        allocations.insert(try allocator.allocate().value)\n        allocations.insert(try allocator.allocate().value)\n        do {\n            _ = try allocator.allocate()\n            #expect(Bool(false), \"Expected AllocatorError.allocatorFull to be thrown\")\n        } catch {\n            #expect(error as? AllocatorError == .allocatorFull, \"Unexpected error thrown: \\(error)\")\n        }\n\n        let address = try IPv4Address(\"192.168.64.2\")\n        try allocator.release(address)\n\n        let value = try allocator.allocate()\n        #expect(value == address)\n    }\n\n    @Test\n    func testHighestIPv4AddressAllocator() throws {\n        var allocations = Set<UInt32>()\n        let lower = try IPv4Address(\"255.255.255.255\").value & Prefix(length: 32)!.prefixMask32\n        let allocator = try IPv4Address.allocator(\n            lower: lower, size: 1)\n        allocations.insert(try allocator.allocate().value)\n        do {\n            _ = try allocator.allocate()\n            #expect(Bool(false), \"Expected AllocatorError.allocatorFull to be thrown\")\n        } catch {\n            #expect(error as? AllocatorError == .allocatorFull, \"Unexpected error thrown: \\(error)\")\n        }\n\n        let address = try IPv4Address(\"255.255.255.255\")\n        try allocator.release(address)\n        let value = try allocator.allocate()\n        #expect(value == address)\n    }\n\n    @Test\n    func testLargestIPv4AddressAllocator() throws {\n        // NOTE: This allocator should consume about 16MB\n        _ = try IPv4Address.allocator(lower: 0, size: 1 << 32)\n    }\n\n    @Test\n    func testUInt16PortAllocator() throws {\n        var allocations = Set<UInt16>()\n        let lower = UInt16(1024)\n        let allocator = try UInt16.allocator(lower: lower, size: 3)\n        allocations.insert(try allocator.allocate())\n        allocations.insert(try allocator.allocate())\n        allocations.insert(try allocator.allocate())\n        do {\n            _ = try allocator.allocate()\n            #expect(Bool(false), \"Expected AllocatorError.allocatorFull to be thrown\")\n        } catch {\n            #expect(error as? AllocatorError == .allocatorFull, \"Unexpected error thrown: \\(error)\")\n        }\n\n        let address = UInt16(1025)\n        try allocator.release(address)\n        let value = try allocator.allocate()\n        #expect(value == address)\n    }\n\n    @Test\n    func testUInt32PortAllocator() throws {\n        var allocations = Set<UInt32>()\n        let lower = UInt32(5000)\n        let allocator = try UInt32.allocator(lower: lower, size: 3)\n        allocations.insert(try allocator.allocate())\n        allocations.insert(try allocator.allocate())\n        allocations.insert(try allocator.allocate())\n        do {\n            _ = try allocator.allocate()\n            #expect(Bool(false), \"Expected AllocatorError.allocatorFull to be thrown\")\n        } catch {\n            #expect(error as? AllocatorError == .allocatorFull, \"Unexpected error thrown: \\(error)\")\n        }\n\n        let address = UInt32(5001)\n        try allocator.release(address)\n        let value = try allocator.allocate()\n        #expect(value == address)\n    }\n\n    @Test\n    func testRotatingUInt32PortAllocator() throws {\n        var allocations = Set<UInt32>()\n        let lower = UInt32(5000)\n        let allocator = try UInt32.rotatingAllocator(lower: lower, size: 3)\n        allocations.insert(try allocator.allocate())\n        allocations.insert(try allocator.allocate())\n        allocations.insert(try allocator.allocate())\n        do {\n            _ = try allocator.allocate()\n            #expect(Bool(false), \"Expected AllocatorError.allocatorFull to be thrown\")\n        } catch {\n            #expect(error as? AllocatorError == .allocatorFull, \"Unexpected error thrown: \\(error)\")\n        }\n\n        let address = UInt32(5001)\n        try allocator.release(address)\n        let value = try allocator.allocate()\n        #expect(value == address)\n    }\n\n    @Test\n    func testRotatingFIFOUInt32PortAllocator() throws {\n        let lower = UInt32(5000)\n        let allocator = try UInt32.rotatingAllocator(lower: lower, size: 3)\n        let first = try allocator.allocate()\n        #expect(first == 5000)\n        let second = try allocator.allocate()\n        #expect(second == 5001)\n\n        try allocator.release(first)\n        let third = try allocator.allocate()\n        // even after a release, it should continue to allocate in the range\n        // before reusing a previous allocation on the stack.\n        #expect(third == 5002)\n\n        // now the next allocation should be our first port\n        let reused = try allocator.allocate()\n        #expect(reused == first)\n\n        try allocator.release(third)\n        let thirdReused = try allocator.allocate()\n        #expect(thirdReused == third)\n    }\n\n    @Test\n    func testRotatingReservedUInt32PortAllocator() throws {\n        let lower = UInt32(5000)\n        let allocator = try UInt32.rotatingAllocator(lower: lower, size: 3)\n\n        try allocator.reserve(5001)\n        let first = try allocator.allocate()\n        #expect(first == 5000)\n        // this should skip the reserved 5001\n        let second = try allocator.allocate()\n        #expect(second == 5002)\n\n        // no release our reserved\n        try allocator.release(5001)\n\n        let third = try allocator.allocate()\n        #expect(third == 5001)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationExtrasTests/TestPrefix.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Testing\n\n@testable import ContainerizationExtras\n\nstruct TestPrefix {\n\n    // MARK: - IPv4 Mask Tests\n\n    struct IPv4Mask {\n        let length: UInt8\n        let expectedPrefix: UInt32\n        let expectedSuffix: UInt32\n    }\n\n    @Test(arguments: [\n        IPv4Mask(\n            length: 0,\n            expectedPrefix: 0x0000_0000,\n            expectedSuffix: 0xFFFF_FFFF\n        ),\n        IPv4Mask(\n            length: 8,\n            expectedPrefix: 0xFF00_0000,\n            expectedSuffix: 0x00FF_FFFF\n        ),\n        IPv4Mask(\n            length: 16,\n            expectedPrefix: 0xFFFF_0000,\n            expectedSuffix: 0x0000_FFFF\n        ),\n        IPv4Mask(\n            length: 24,\n            expectedPrefix: 0xFFFF_FF00,\n            expectedSuffix: 0x0000_00FF\n        ),\n        IPv4Mask(\n            length: 32,\n            expectedPrefix: 0xFFFF_FFFF,\n            expectedSuffix: 0x0000_0000\n        ),\n    ])\n    func testIPv4Masks(testCase: IPv4Mask) {\n        let prefix = Prefix(length: testCase.length)!\n        #expect(prefix.prefixMask32 == testCase.expectedPrefix)\n        #expect(prefix.suffixMask32 == testCase.expectedSuffix)\n    }\n\n    @Test func testMasksAreInverses32() {\n        for length in 0...32 {\n            let prefix = Prefix(length: UInt8(length))!\n            #expect(~prefix.prefixMask32 == prefix.suffixMask32)\n        }\n    }\n\n    // MARK: - IPv6 Mask Tests\n\n    struct IPv6Mask {\n        let length: UInt8\n        let expectedPrefix: UInt128\n        let expectedSuffix: UInt128\n    }\n\n    @Test func testIPv6Masks() {\n        let cases = [\n            IPv6Mask(\n                length: 0,\n                expectedPrefix: UInt128(0),\n                expectedSuffix: UInt128.max\n            ),\n            IPv6Mask(\n                length: 64,\n                expectedPrefix: 0xFFFF_FFFF_FFFF_FFFF_0000_0000_0000_0000,\n                expectedSuffix: 0x0000_0000_0000_0000_FFFF_FFFF_FFFF_FFFF\n            ),\n            IPv6Mask(\n                length: 128,\n                expectedPrefix: UInt128.max,\n                expectedSuffix: UInt128(0)\n            ),\n        ]\n        for testCase in cases {\n            let prefix = Prefix(length: testCase.length)!\n            #expect(prefix.prefixMask128 == testCase.expectedPrefix)\n            #expect(prefix.suffixMask128 == testCase.expectedSuffix)\n        }\n    }\n\n    @Test func testMasksAreInverses128() {\n        for length in stride(from: 0, through: 128, by: 8) {\n            let prefix = Prefix(length: UInt8(length))!\n            #expect(~prefix.prefixMask128 == prefix.suffixMask128)\n        }\n    }\n\n    // MARK: - Description Tests\n\n    @Test func testDescription() {\n        #expect(Prefix(length: 24)!.description == \"24\")\n        #expect(Prefix(length: 0)!.description == \"0\")\n        #expect(Prefix(length: 128)!.description == \"128\")\n    }\n\n    // MARK: - Validation Tests\n\n    @Test func testValidationRejectsInvalidLengths() {\n        // Valid ranges\n        #expect(Prefix(length: 0) != nil)\n        #expect(Prefix(length: 32) != nil)\n        #expect(Prefix(length: 128) != nil)\n\n        // Invalid ranges\n        #expect(Prefix(length: 129) == nil)\n        #expect(Prefix(length: 200) == nil)\n        #expect(Prefix(length: 255) == nil)\n    }\n\n    @Test func testIPv4SpecificValidation() {\n        // Valid IPv4 prefixes\n        #expect(Prefix.ipv4(0) != nil)\n        #expect(Prefix.ipv4(16) != nil)\n        #expect(Prefix.ipv4(32) != nil)\n\n        // Invalid IPv4 prefixes\n        #expect(Prefix.ipv4(33) == nil)\n        #expect(Prefix.ipv4(64) == nil)\n        #expect(Prefix.ipv4(128) == nil)\n    }\n\n    @Test func testIPv6SpecificValidation() {\n        // Valid IPv6 prefixes\n        #expect(Prefix.ipv6(0) != nil)\n        #expect(Prefix.ipv6(64) != nil)\n        #expect(Prefix.ipv6(128) != nil)\n\n        // Invalid IPv6 prefixes\n        #expect(Prefix.ipv6(129) == nil)\n        #expect(Prefix.ipv6(255) == nil)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationExtrasTests/TestTimeout.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationExtras\n\nfinal class TestTimeoutType {\n    @Test\n    func testNoCancellation() async throws {\n        await #expect(throws: Never.self) {\n            try await Timeout.run(\n                for: .seconds(5),\n                operation: {\n                    return\n                })\n        }\n    }\n\n    @Test\n    func testCancellationError() async throws {\n        await #expect(throws: CancellationError.self) {\n            try await Timeout.run(\n                for: .milliseconds(50),\n                operation: {\n                    try await Task.sleep(for: .seconds(2))\n                })\n        }\n    }\n\n    @Test\n    func testClosureError() async throws {\n        // Check that we get the closures error if we don't timeout, but\n        // the closure does throw before.\n        await #expect(throws: POSIXError.self) {\n            try await Timeout.run(\n                for: .seconds(10),\n                operation: {\n                    throw POSIXError(.E2BIG)\n                })\n        }\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationExtrasTests/UInt8+DataBindingTest.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Testing\n\n@testable import ContainerizationExtras\n\nstruct BufferTest {\n    // MARK: - hexEncodedString Tests\n\n    @Test func testArrayHexEncodedStringEmpty() {\n        let buffer: [UInt8] = []\n        #expect(buffer.hexEncodedString() == \"\")\n    }\n\n    @Test func testArrayHexEncodedStringSingleByte() {\n        let buffer: [UInt8] = [0xFF]\n        #expect(buffer.hexEncodedString() == \"ff\")\n    }\n\n    @Test func testArrayHexEncodedStringMultipleBytes() {\n        let buffer: [UInt8] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF]\n        #expect(buffer.hexEncodedString() == \"0123456789abcdef\")\n    }\n\n    @Test func testArrayHexEncodedStringZeroes() {\n        let buffer: [UInt8] = [0x00, 0x00, 0x00]\n        #expect(buffer.hexEncodedString() == \"000000\")\n    }\n\n    @Test func testArraySliceHexEncodedStringEmpty() {\n        let buffer: [UInt8] = [0x01, 0x02, 0x03]\n        let slice = buffer[0..<0]\n        #expect(slice.hexEncodedString() == \"\")\n    }\n\n    @Test func testArraySliceHexEncodedStringSingleByte() {\n        let buffer: [UInt8] = [0x01, 0x02, 0x03]\n        let slice = buffer[1..<2]\n        #expect(slice.hexEncodedString() == \"02\")\n    }\n\n    @Test func testArraySliceHexEncodedStringMultipleBytes() {\n        let buffer: [UInt8] = [0x00, 0xAA, 0xBB, 0xCC, 0x00]\n        let slice = buffer[1..<4]\n        #expect(slice.hexEncodedString() == \"aabbcc\")\n    }\n\n    // MARK: - bind<T> Tests\n\n    @Test func testBufferBind() throws {\n        let expectedValue: UInt64 = 0x0102_0304_0506_0708\n        let expectedBuffer: [UInt8] = [\n            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n            0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,\n        ]\n        var buffer = [UInt8](repeating: 0, count: 3 * MemoryLayout<UInt64>.size)\n        guard let ptr = buffer.bind(as: UInt64.self, offset: 2 * MemoryLayout<UInt64>.size) else {\n            #expect(Bool(false), \"could not bind value to buffer\")\n            return\n        }\n\n        ptr.pointee = expectedValue\n        #expect(buffer == expectedBuffer)\n    }\n\n    @Test func testBufferBindZeroOffset() {\n        let expectedValue: UInt32 = 0x1234_5678\n        var buffer = [UInt8](repeating: 0, count: 8)\n        guard let ptr = buffer.bind(as: UInt32.self, offset: 0) else {\n            #expect(Bool(false), \"could not bind value to buffer at offset 0\")\n            return\n        }\n\n        ptr.pointee = expectedValue\n        #expect(buffer[0] == 0x78)\n        #expect(buffer[1] == 0x56)\n        #expect(buffer[2] == 0x34)\n        #expect(buffer[3] == 0x12)\n    }\n\n    @Test func testBufferBindRangeError() throws {\n        var buffer = [UInt8](repeating: 0, count: 3 * MemoryLayout<UInt64>.size)\n        #expect(buffer.bind(as: UInt64.self, offset: 2 * MemoryLayout<UInt64>.size + 1) == nil)\n    }\n\n    @Test func testBufferBindRangeErrorExactBoundary() {\n        var buffer = [UInt8](repeating: 0, count: 8)\n        // Trying to bind UInt64 at offset 1 requires 9 bytes total\n        #expect(buffer.bind(as: UInt64.self, offset: 1) == nil)\n    }\n\n    @Test func testBufferBindWithCustomSize() {\n        var buffer = [UInt8](repeating: 0, count: 16)\n        // Request a size larger than the type\n        guard let ptr = buffer.bind(as: UInt32.self, offset: 4, size: 8) else {\n            #expect(Bool(false), \"could not bind with custom size\")\n            return\n        }\n\n        ptr.pointee = 0xAABB_CCDD\n        #expect(buffer[4] == 0xDD)\n        #expect(buffer[5] == 0xCC)\n        #expect(buffer[6] == 0xBB)\n        #expect(buffer[7] == 0xAA)\n    }\n\n    @Test func testBufferBindWithCustomSizeRangeError() {\n        var buffer = [UInt8](repeating: 0, count: 10)\n        // Request size 8 at offset 4 would require 12 bytes total\n        #expect(buffer.bind(as: UInt32.self, offset: 4, size: 8) == nil)\n    }\n\n    // MARK: - copyIn<T> Tests\n\n    @Test func testCopyInUInt8() {\n        var buffer = [UInt8](repeating: 0, count: 4)\n        let value: UInt8 = 0x42\n\n        guard let offset = buffer.copyIn(as: UInt8.self, value: value, offset: 2) else {\n            #expect(Bool(false), \"could not copy UInt8 to buffer\")\n            return\n        }\n\n        #expect(offset == 3)\n        #expect(buffer[2] == 0x42)\n    }\n\n    @Test func testCopyInUInt16() {\n        var buffer = [UInt8](repeating: 0, count: 8)\n        let value: UInt16 = 0x1234\n\n        guard let offset = buffer.copyIn(as: UInt16.self, value: value, offset: 3) else {\n            #expect(Bool(false), \"could not copy UInt16 to buffer\")\n            return\n        }\n\n        #expect(offset == 5)\n        #expect(buffer[3] == 0x34)\n        #expect(buffer[4] == 0x12)\n    }\n\n    @Test func testCopyInUInt32() {\n        var buffer = [UInt8](repeating: 0, count: 8)\n        let value: UInt32 = 0x1234_5678\n\n        guard let offset = buffer.copyIn(as: UInt32.self, value: value, offset: 0) else {\n            #expect(Bool(false), \"could not copy UInt32 to buffer\")\n            return\n        }\n\n        #expect(offset == 4)\n        #expect(buffer[0] == 0x78)\n        #expect(buffer[1] == 0x56)\n        #expect(buffer[2] == 0x34)\n        #expect(buffer[3] == 0x12)\n    }\n\n    @Test func testCopyInUInt64() {\n        var buffer = [UInt8](repeating: 0, count: 16)\n        let value: UInt64 = 0x0102_0304_0506_0708\n\n        guard let offset = buffer.copyIn(as: UInt64.self, value: value, offset: 4) else {\n            #expect(Bool(false), \"could not copy UInt64 to buffer\")\n            return\n        }\n\n        #expect(offset == 12)\n        #expect(buffer[4] == 0x08)\n        #expect(buffer[5] == 0x07)\n        #expect(buffer[6] == 0x06)\n        #expect(buffer[7] == 0x05)\n        #expect(buffer[8] == 0x04)\n        #expect(buffer[9] == 0x03)\n        #expect(buffer[10] == 0x02)\n        #expect(buffer[11] == 0x01)\n    }\n\n    @Test func testCopyInRangeError() {\n        var buffer = [UInt8](repeating: 0, count: 8)\n        let value: UInt64 = 0x1234_5678_90AB_CDEF\n\n        // Offset 4 + size 8 = 12, but buffer only has 8 bytes\n        #expect(buffer.copyIn(as: UInt64.self, value: value, offset: 4) == nil)\n    }\n\n    @Test func testCopyInExactBoundary() {\n        var buffer = [UInt8](repeating: 0, count: 8)\n        let value: UInt64 = 0xFEDC_BA98_7654_3210\n\n        guard let offset = buffer.copyIn(as: UInt64.self, value: value, offset: 0) else {\n            #expect(Bool(false), \"could not copy UInt64 at exact boundary\")\n            return\n        }\n\n        #expect(offset == 8)\n    }\n\n    @Test func testCopyInWithCustomSize() {\n        var buffer = [UInt8](repeating: 0, count: 16)\n        let value: UInt32 = 0xAABB_CCDD\n\n        // Copy with custom size of 8 (larger than UInt32's 4 bytes)\n        guard let offset = buffer.copyIn(as: UInt32.self, value: value, offset: 2, size: 8) else {\n            #expect(Bool(false), \"could not copy with custom size\")\n            return\n        }\n\n        #expect(offset == 6)  // offset + MemoryLayout<UInt32>.size\n        #expect(buffer[2] == 0xDD)\n        #expect(buffer[3] == 0xCC)\n        #expect(buffer[4] == 0xBB)\n        #expect(buffer[5] == 0xAA)\n    }\n\n    @Test func testCopyInWithCustomSizeRangeError() {\n        var buffer = [UInt8](repeating: 0, count: 8)\n        let value: UInt32 = 0x1234_5678\n\n        // Request size 8 at offset 2 would require 10 bytes total\n        #expect(buffer.copyIn(as: UInt32.self, value: value, offset: 2, size: 8) == nil)\n    }\n\n    // MARK: - copyOut<T> Tests\n\n    @Test func testCopyOutUInt8() {\n        let buffer: [UInt8] = [0x00, 0x11, 0x22, 0x33]\n\n        guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: 2) else {\n            #expect(Bool(false), \"could not copy out UInt8\")\n            return\n        }\n\n        #expect(offset == 3)\n        #expect(value == 0x22)\n    }\n\n    @Test func testCopyOutUInt16() {\n        let buffer: [UInt8] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]\n\n        guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: 2) else {\n            #expect(Bool(false), \"could not copy out UInt16\")\n            return\n        }\n\n        #expect(offset == 4)\n        #expect(value == 0x3322)\n    }\n\n    @Test func testCopyOutUInt32() {\n        let buffer: [UInt8] = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]\n\n        guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: 0) else {\n            #expect(Bool(false), \"could not copy out UInt32\")\n            return\n        }\n\n        #expect(offset == 4)\n        #expect(value == 0x7856_3412)\n    }\n\n    @Test func testCopyOutUInt64() {\n        let buffer: [UInt8] = [\n            0x00, 0x00, 0x00, 0x00,\n            0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,\n            0xFF, 0xFF,\n        ]\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: 4) else {\n            #expect(Bool(false), \"could not copy out UInt64\")\n            return\n        }\n\n        #expect(offset == 12)\n        #expect(value == 0x8877_6655_4433_2211)\n    }\n\n    @Test func testCopyOutRangeError() {\n        let buffer: [UInt8] = [0x00, 0x11, 0x22, 0x33]\n\n        // Trying to read UInt64 from offset 0 with only 4 bytes\n        #expect(buffer.copyOut(as: UInt64.self, offset: 0) == nil)\n    }\n\n    @Test func testCopyOutExactBoundary() {\n        let buffer: [UInt8] = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]\n\n        guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: 0) else {\n            #expect(Bool(false), \"could not copy out at exact boundary\")\n            return\n        }\n\n        #expect(offset == 8)\n        #expect(value == 0x0807_0605_0403_0201)\n    }\n\n    @Test func testCopyOutWithCustomSize() {\n        let buffer: [UInt8] = [0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0xFF, 0xFF, 0xFF, 0xFF]\n\n        guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: 2, size: 8) else {\n            #expect(Bool(false), \"could not copy out with custom size\")\n            return\n        }\n\n        #expect(offset == 6)  // offset + MemoryLayout<UInt32>.size\n        #expect(value == 0x4433_2211)\n    }\n\n    @Test func testCopyOutWithCustomSizeRangeError() {\n        let buffer: [UInt8] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]\n\n        // Request size 8 at offset 2 would require 10 bytes total, but buffer only has 6\n        #expect(buffer.copyOut(as: UInt32.self, offset: 2, size: 8) == nil)\n    }\n\n    // MARK: - copyIn(buffer:) and copyOut(buffer:) Tests\n\n    @Test func testBufferCopy() throws {\n        let inputBuffer: [UInt8] = [0x01, 0x02, 0x03]\n        var buffer = [UInt8](repeating: 0, count: 9)\n\n        guard let offset = buffer.copyIn(buffer: inputBuffer, offset: 4) else {\n            #expect(Bool(false), \"could not copy to buffer\")\n            return\n        }\n        #expect(offset == 7)\n\n        guard let offset = buffer.copyIn(buffer: inputBuffer, offset: 6) else {\n            #expect(Bool(false), \"could not copy to buffer\")\n            return\n        }\n        #expect(offset == 9)\n\n        let expectedBuffer: [UInt8] = [\n            0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0x02, 0x03,\n        ]\n        #expect(expectedBuffer == buffer)\n\n        var outputBuffer = [UInt8](repeating: 0, count: 3)\n        guard let offset = buffer.copyOut(buffer: &outputBuffer, offset: 6) else {\n            #expect(Bool(false), \"could not copy to buffer\")\n            return\n        }\n        #expect(offset == 9)\n\n        let expectedOutputBuffer: [UInt8] = [\n            0x01, 0x02, 0x03,\n        ]\n        #expect(expectedOutputBuffer == outputBuffer)\n    }\n\n    @Test func testBufferCopyZeroOffset() {\n        let inputBuffer: [UInt8] = [0xAA, 0xBB, 0xCC]\n        var buffer = [UInt8](repeating: 0, count: 5)\n\n        guard let offset = buffer.copyIn(buffer: inputBuffer, offset: 0) else {\n            #expect(Bool(false), \"could not copy to buffer at offset 0\")\n            return\n        }\n\n        #expect(offset == 3)\n        #expect(buffer[0] == 0xAA)\n        #expect(buffer[1] == 0xBB)\n        #expect(buffer[2] == 0xCC)\n    }\n\n    @Test func testBufferCopyEmptyBuffer() {\n        let inputBuffer: [UInt8] = []\n        var buffer = [UInt8](repeating: 0, count: 5)\n\n        guard let offset = buffer.copyIn(buffer: inputBuffer, offset: 2) else {\n            #expect(Bool(false), \"could not copy empty buffer\")\n            return\n        }\n\n        #expect(offset == 2)\n    }\n\n    @Test func testBufferCopyExactFit() {\n        let inputBuffer: [UInt8] = [0x01, 0x02, 0x03]\n        var buffer = [UInt8](repeating: 0, count: 6)\n\n        guard let offset = buffer.copyIn(buffer: inputBuffer, offset: 3) else {\n            #expect(Bool(false), \"could not copy to exact fit\")\n            return\n        }\n\n        #expect(offset == 6)\n    }\n\n    @Test func testBufferCopyRangeError() throws {\n        let inputBuffer: [UInt8] = [0x01, 0x02, 0x03]\n        var buffer = [UInt8](repeating: 0, count: 9)\n\n        #expect(buffer.copyIn(buffer: inputBuffer, offset: 7) == nil)\n\n        var outputBuffer = [UInt8](repeating: 0, count: 3)\n        #expect(buffer.copyOut(buffer: &outputBuffer, offset: 7) == nil)\n    }\n\n    @Test func testBufferCopyOutZeroOffset() {\n        let buffer: [UInt8] = [0x11, 0x22, 0x33, 0x44, 0x55]\n        var outputBuffer = [UInt8](repeating: 0, count: 3)\n\n        guard let offset = buffer.copyOut(buffer: &outputBuffer, offset: 0) else {\n            #expect(Bool(false), \"could not copy out at offset 0\")\n            return\n        }\n\n        #expect(offset == 3)\n        #expect(outputBuffer[0] == 0x11)\n        #expect(outputBuffer[1] == 0x22)\n        #expect(outputBuffer[2] == 0x33)\n    }\n\n    @Test func testBufferCopyOutEmptyBuffer() {\n        let buffer: [UInt8] = [0x11, 0x22, 0x33]\n        var outputBuffer: [UInt8] = []\n\n        guard let offset = buffer.copyOut(buffer: &outputBuffer, offset: 1) else {\n            #expect(Bool(false), \"could not copy out to empty buffer\")\n            return\n        }\n\n        #expect(offset == 1)\n    }\n\n    @Test func testBufferCopyOutExactFit() {\n        let buffer: [UInt8] = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]\n        var outputBuffer = [UInt8](repeating: 0, count: 3)\n\n        guard let offset = buffer.copyOut(buffer: &outputBuffer, offset: 3) else {\n            #expect(Bool(false), \"could not copy out exact fit\")\n            return\n        }\n\n        #expect(offset == 6)\n        #expect(outputBuffer[0] == 0xDD)\n        #expect(outputBuffer[1] == 0xEE)\n        #expect(outputBuffer[2] == 0xFF)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationNetlinkTests/MockNetlinkSocket.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//\n\n@testable import ContainerizationNetlink\n\nclass MockNetlinkSocket: NetlinkSocket {\n    static let ENOMEM: Int32 = 12\n    static let EOVERFLOW: Int32 = 75\n\n    var pid: UInt32 = 0\n\n    var requests: [[UInt8]] = []\n    var responses: [[UInt8]] = []\n\n    var responseIndex = 0\n\n    public init() throws {}\n\n    public func send(buf: UnsafeRawPointer!, len: Int, flags: Int32) throws -> Int {\n        let ptr = buf.bindMemory(to: UInt8.self, capacity: len)\n        requests.append(Array(UnsafeBufferPointer(start: ptr, count: len)))\n        return len\n    }\n\n    public func recv(buf: UnsafeMutableRawPointer!, len: Int, flags: Int32) throws -> Int {\n        guard responseIndex < responses.count else {\n            throw NetlinkSocketError.recvFailure(rc: Self.ENOMEM)\n        }\n\n        let response = responses[responseIndex]\n        guard len >= response.count else {\n            throw NetlinkSocketError.recvFailure(rc: 75)\n        }\n\n        response.withUnsafeBytes { bytes in\n            buf.copyMemory(from: bytes.baseAddress!, byteCount: response.count)\n        }\n\n        responseIndex += 1\n        return response.count\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationNetlinkTests/NetlinkSessionTest.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationExtras\nimport ContainerizationOS\nimport Testing\n\n@testable import ContainerizationNetlink\n\nstruct NetlinkSessionTest {\n    @Test func testNetworkLinkDown() throws {\n        let mockSocket = try MockNetlinkSocket()\n        mockSocket.pid = 0xc00c_c00c\n\n        // Lookup interface by name, truncated response with no attributes (not needed at present).\n        let expectedLookupRequest =\n            \"3400000012000100000000000cc00cc0\"  // Netlink header (16 B)\n            + \"110000000000000001000000ffffffff\"  // struct ifinfomsg (16 B)\n            + \"08001d00090000000c0003006574683000000000\"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“eth0”)\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"2000000010000000000000000cc00cc0\"  // Netlink header (16 B)\n                    + \"00000100020000004310010000000000\"  // struct ifinfomsg (16 B) – no RT attrs\n            )\n        )\n\n        // Link‑down request – 32‑byte payload, no attributes.\n        let expectedDownRequest =\n            \"2000000010000500000000000cc00cc0\"  // Netlink header (16 B)\n            + \"110000000200000000000000ffffffff\"  // struct ifinfomsg (16 B) – no RT attrs\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"2400000002000001000000000cc00cc0\"  // Netlink header (16 B)\n                    + \"00000000200000001000050000000000\"  // nlmsg_err payload (16 B)\n                    + \"0c000000\"  // first 4 B of echoed header\n            )\n        )\n\n        let session = NetlinkSession(socket: mockSocket)\n        try session.linkSet(interface: \"eth0\", up: false)\n\n        #expect(mockSocket.requests.count == 2)\n        #expect(mockSocket.responseIndex == 2)\n        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]\n        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())\n        mockSocket.requests[1][8..<12] = [0, 0, 0, 0]\n        #expect(expectedDownRequest == mockSocket.requests[1].hexEncodedString())\n    }\n\n    @Test func testNetworkLinkUp() throws {\n        let mockSocket = try MockNetlinkSocket()\n        mockSocket.pid = 0x0cc0_0cc0\n\n        // Lookup interface by name, truncated response with no attributes (not needed at present).\n        let expectedLookupRequest =\n            \"340000001200010000000000c00cc00c\"  // Netlink header (16 B)\n            + \"110000000000000001000000ffffffff\"  // struct ifinfomsg (16 B)\n            + \"08001d00090000000c0003006574683000000000\"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“eth0”)\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"200000001000000000000000c00cc00c\"  // Netlink header (16 B)\n                    + \"00000100020000004310010000000000\"  // struct ifinfomsg (16 B) – no attributes\n            )\n        )\n\n        // Network up for interface.\n        let expectedUpRequest =\n            \"280000001000050000000000c00cc00c\"  // Netlink header (16 B)\n            + \"110000000200000001000000ffffffff\"  // struct ifinfomsg (16 B)\n            + \"0800040000050000\"  // RT attr: IFLA_MTU = 1280 (8 B)\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"240000000200000100000000c00cc00c\"  // Netlink header (16 B)\n                    + \"00000000200000001000050000000000\"  // nlmsg_err payload (16 B)\n                    + \"11000000\"  // 1st 4 B of echoed offending header\n            )\n        )\n\n        let session = NetlinkSession(socket: mockSocket)\n        try session.linkSet(interface: \"eth0\", up: true, mtu: 1280)\n\n        #expect(mockSocket.requests.count == 2)\n        #expect(mockSocket.responseIndex == 2)\n        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]\n        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())\n        mockSocket.requests[1][8..<12] = [0, 0, 0, 0]\n        #expect(expectedUpRequest == mockSocket.requests[1].hexEncodedString())\n    }\n\n    @Test func testNetworkLinkUpLoopback() throws {\n        let mockSocket = try MockNetlinkSocket()\n        mockSocket.pid = 0xc00c_c00c\n\n        // Lookup loopback interface\n        let expectedLookupRequest =\n            \"3000000012000100000000000cc00cc0\"  // Netlink header (16 B)\n            + \"110000000000000001000000ffffffff\"  // struct ifinfomsg (16 B)\n            + \"08001d0009000000080003006c6f0000\"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“lo”)\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"2000000010000000000000000cc00cc0\"  // Netlink header (16 B)\n                    + \"00000100010000004310010000000000\"  // struct ifinfomsg (16 B) – no attributes\n            )\n        )\n\n        // Link up request for loopback, 32‑byte payload and no attributes\n        let expectedUpRequest =\n            \"2000000010000500000000000cc00cc0\"  // Netlink header (16 B)\n            + \"110000000100000001000000ffffffff\"  // struct ifinfomsg (16 B) – no RT attrs\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"2400000002000001000000000cc00cc0\"  // Netlink header (16 B)\n                    + \"00000000200000001000050000000000\"  // nlmsg_err payload (16 B)\n                    + \"0c000000\"  // first 4 B of echoed offending header\n            )\n        )\n\n        let session = NetlinkSession(socket: mockSocket)\n        try session.linkSet(interface: \"lo\", up: true)\n\n        #expect(mockSocket.requests.count == 2)\n        #expect(mockSocket.responseIndex == 2)\n        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]\n        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())\n        mockSocket.requests[1][8..<12] = [0, 0, 0, 0]\n        #expect(expectedUpRequest == mockSocket.requests[1].hexEncodedString())\n    }\n\n    @Test func testNetworkLinkGetEth0() throws {\n        let mockSocket = try MockNetlinkSocket()\n        mockSocket.pid = 0x1234_5678\n\n        // Lookup interface by name, truncated response with three attributes.\n        let expectedLookupRequest =\n            \"34000000120001000000000078563412\"  // Netlink header (16 B)\n            + \"110000000000000001000000ffffffff\"  // struct ifinfomsg (16 B)\n            + \"08001d00090000000c0003006574683000000000\"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“eth0”)\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"48000000100000000000000078563412\"  // Netlink header (16 B)\n                    + \"00000100020000004300010000000000\"  // struct ifinfomsg (16 B)\n                    + \"090003006574683000000000\"  // IFLA_IFNAME (“eth0”) attr (12 B)\n                    + \"08000d00e8030000\"  // IFLA_MTU = 1000 attr (8 B)\n                    + \"0500100006000000\"  // attr type 0x0010 (8 B)\n                    + \"0a000100825524c244030000\"  // IFLA_ADDRESS = 82:55:24:c2:44:03 (12 B)\n            )\n        )\n\n        let session = NetlinkSession(socket: mockSocket)\n        let links = try session.linkGet(interface: \"eth0\")\n\n        #expect(mockSocket.requests.count == 1)\n        #expect(mockSocket.responseIndex == 1)\n        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]\n        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())\n        try #require(links.count == 1)\n\n        #expect(links[0].interfaceIndex == 2)\n        #expect(links[0].interfaceFlags == 0x0001_0043)\n        #expect(links[0].interfaceType == 1)\n        #expect(links[0].isEthernet)\n        #expect(!links[0].isLoopback)\n        #expect(links[0].address == [0x82, 0x55, 0x24, 0xc2, 0x44, 0x03])\n        try #require(links[0].attrDatas.count == 4)\n        #expect(links[0].attrDatas[0].attribute.type == 0x0003)\n        #expect(links[0].attrDatas[0].attribute.len == 0x0009)\n        #expect(links[0].attrDatas[0].data == [0x65, 0x74, 0x68, 0x30, 0x00])\n        #expect(links[0].attrDatas[1].attribute.type == 0x000d)\n        #expect(links[0].attrDatas[1].attribute.len == 0x0008)\n        #expect(links[0].attrDatas[1].data == [0xe8, 0x03, 0x00, 0x00])\n        #expect(links[0].attrDatas[2].attribute.type == 0x0010)\n        #expect(links[0].attrDatas[2].attribute.len == 0x0005)\n        #expect(links[0].attrDatas[2].data == [0x06])\n    }\n\n    @Test func testNetworkLinkGet() throws {\n        let mockSocket = try MockNetlinkSocket()\n        mockSocket.pid = 0x8765_4321\n\n        // Lookup all interfaces, responses with only the interface name attribute.\n        let expectedLookupRequest =\n            \"28000000120001030000000021436587\"  // Netlink header (16 B)\n            + \"110000000000000001000000ffffffff\"  // struct ifinfomsg (16 B)\n            + \"08001d0009000000\"  // RT attr: IFLA_EXT_MASK (8 B)\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"28000000100002000000000021436587\"  // Netlink header (16 B)\n                    + \"00000403010000004900010000000000\"  // struct ifinfomsg (16 B)\n                    + \"070003006c6f0000\"  // IFLA_IFNAME “lo” (8 B, padded)\n            )\n        )\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"2c000000100002000000000021436587\"  // Netlink header (16 B)\n                    + \"00000003040000008000000000000000\"  // struct ifinfomsg (16 B)\n                    + \"0a00030074756e6c30000000\"  // IFLA_IFNAME “tunl0” attr (12 B, padded)\n            )\n        )\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"14000000030002000000000021436587\"  // Netlink header (16 B) – NLMSG_DONE\n                    + \"00000000\"  // 4-byte payload\n            )\n        )\n\n        let session = NetlinkSession(socket: mockSocket)\n        let links = try session.linkGet()\n\n        #expect(mockSocket.requests.count == 1)\n        #expect(mockSocket.responseIndex == 3)\n        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]\n        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())\n        try #require(links.count == 2)\n\n        #expect(links[0].interfaceIndex == 1)\n        try #require(links[0].attrDatas.count == 1)\n        #expect(links[0].attrDatas[0].attribute.type == 0x0003)\n        #expect(links[0].attrDatas[0].attribute.len == 0x0007)\n        #expect(links[0].attrDatas[0].data == [0x6c, 0x6f, 0x00])\n\n        #expect(links[1].interfaceIndex == 4)\n        try #require(links[1].attrDatas.count == 1)\n        #expect(links[1].attrDatas[0].attribute.type == 0x0003)\n        #expect(links[1].attrDatas[0].attribute.len == 0x000a)\n        #expect(links[1].attrDatas[0].data == [0x74, 0x75, 0x6e, 0x6c, 0x30, 0x00])\n    }\n\n    @Test func testNetworkAddressAdd() throws {\n        let mockSocket = try MockNetlinkSocket()\n        mockSocket.pid = 0xc00c_c00c\n\n        // Lookup interface by name, truncated response with no attributes (not needed at present).\n        let expectedLookupRequest =\n            \"3400000012000100000000000cc00cc0\"  // Netlink header (16 B)\n            + \"110000000000000001000000ffffffff\"  // struct ifinfomsg (16 B)\n            + \"08001d00090000000c0003006574683000000000\"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (“eth0”)\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"2000000010000000000000000cc00cc0\"  // Netlink header (16 B)\n                    + \"00000100020000004310010000000000\"  // struct ifinfomsg (16 B) – no attributes\n            )\n        )\n\n        // Network down for interface.\n        let expectedAddRequest =\n            \"2800000014000506000000000cc00cc0\"  // Netlink header (16 B)\n            + \"0218000002000000\"  // ifaddrmsg (8 B): AF_INET, /24, ifindex 2\n            + \"08000200c0a840fa\"  // RT attr: IFA_LOCAL    192.168.64.250\n            + \"08000100c0a840fa\"  // RT attr: IFA_ADDRESS  192.168.64.250\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"2400000002000001000000000cc00cc0\"  // Netlink header (16 B)\n                    + \"00000000280000001400050600000000\"  // nlmsg_err payload (16 B)\n                    + \"1f000000\"  // first 4 B of echoed offending header\n            )\n        )\n\n        let session = NetlinkSession(socket: mockSocket)\n        try session.addressAdd(interface: \"eth0\", ipv4Address: try CIDRv4(\"192.168.64.250/24\"))\n\n        #expect(mockSocket.requests.count == 2)\n        #expect(mockSocket.responseIndex == 2)\n        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]\n        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())\n        #expect(expectedAddRequest == mockSocket.requests[1].hexEncodedString())\n    }\n\n    @Test func testNetworkRouteAddIpLink() throws {\n        let mockSocket = try MockNetlinkSocket()\n        mockSocket.pid = 0xc00c_c00c\n\n        // Lookup interface by name, truncated response with no attributes (not needed at present).\n        let expectedLookupRequest =\n            \"3400000012000100000000000cc00cc0\"  // Netlink header (16 B)\n            + \"110000000000000001000000ffffffff\"  // struct ifinfomsg (16 B)\n            + \"08001d00090000000c0003006574683000000000\"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (\"eth0\")\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"2000000010000000000000000cc00cc0\"  // Netlink header (16 B)\n                    + \"00000100020000004310010000000000\"  // struct ifinfomsg (16 B) – no attributes\n            )\n        )\n\n        // Add link route.\n        let expectedAddRequest =\n            \"3400000018000506000000000cc00cc0\"  // Netlink header (16 B)\n            + \"02180000fe02fd0100000000\"  // struct rtmsg (12 B): AF_INET, dst/24,\n            //   table=RT_TABLE_MAIN (0xfe), proto=RTPROT_KERNEL (0x02),\n            //   scope=RT_SCOPE_LINK (0xfd), type=RTN_UNICAST (0x01)\n            + \"08000100c0a84000\"  // RTA_DST     192.168.64.0\n            + \"08000700c0a84003\"  // RTA_PREFSRC 192.168.64.3\n            + \"0800040002000000\"  // RTA_OIF     ifindex 2 (eth0)\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"2400000002000001000000000cc00cc0\"  // Netlink header (16 B)\n                    + \"00000000280000001400050600000000\"  // nlmsg_err payload (16 B)\n                    + \"1f000000\"  // first 4 B of echoed offending header\n            )\n        )\n\n        let session = NetlinkSession(socket: mockSocket)\n        try session.routeAdd(\n            interface: \"eth0\",\n            dstIpv4Addr: try CIDRv4(\"192.168.64.0/24\"),\n            srcIpv4Addr: try IPv4Address(\"192.168.64.3\")\n        )\n\n        #expect(mockSocket.requests.count == 2)\n        #expect(mockSocket.responseIndex == 2)\n        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]\n        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())\n        mockSocket.requests[1][8..<12] = [0, 0, 0, 0]\n        #expect(expectedAddRequest == mockSocket.requests[1].hexEncodedString())\n    }\n\n    @Test func testNetworkRouteAddIpLinkWithoutSrc() throws {\n        let mockSocket = try MockNetlinkSocket()\n        mockSocket.pid = 0xc00c_c00c\n\n        // Lookup interface by name, truncated response with no attributes (not needed at present).\n        let expectedLookupRequest =\n            \"3400000012000100000000000cc00cc0\"  // Netlink header (16 B)\n            + \"110000000000000001000000ffffffff\"  // struct ifinfomsg (16 B)\n            + \"08001d00090000000c0003006574683000000000\"  // RT attrs: IFLA_EXT_MASK + IFLA_IFNAME (\"eth0\")\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"2000000010000000000000000cc00cc0\"  // Netlink header (16 B)\n                    + \"00000100020000004310010000000000\"  // struct ifinfomsg (16 B) – no attributes\n            )\n        )\n\n        // Add link route without RTA_PREFSRC.\n        let expectedAddRequest =\n            \"2c00000018000506000000000cc00cc0\"  // Netlink header (16 B)\n            + \"02180000fe02fd0100000000\"  // struct rtmsg (12 B): AF_INET, dst/24,\n            //   table=RT_TABLE_MAIN (0xfe), proto=RTPROT_KERNEL (0x02),\n            //   scope=RT_SCOPE_LINK (0xfd), type=RTN_UNICAST (0x01)\n            + \"08000100c0a84000\"  // RTA_DST     192.168.64.0\n            + \"0800040002000000\"  // RTA_OIF     ifindex 2 (eth0)\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"2400000002000001000000000cc00cc0\"  // Netlink header (16 B)\n                    + \"00000000280000001400050600000000\"  // nlmsg_err payload (16 B)\n                    + \"1f000000\"  // first 4 B of echoed offending header\n            )\n        )\n\n        let session = NetlinkSession(socket: mockSocket)\n        try session.routeAdd(\n            interface: \"eth0\",\n            dstIpv4Addr: try CIDRv4(\"192.168.64.0/24\"),\n            srcIpv4Addr: nil\n        )\n\n        #expect(mockSocket.requests.count == 2)\n        #expect(mockSocket.responseIndex == 2)\n        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]\n        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())\n        mockSocket.requests[1][8..<12] = [0, 0, 0, 0]\n        #expect(expectedAddRequest == mockSocket.requests[1].hexEncodedString())\n    }\n\n    @Test func testNetworkLinkGetMultipleMessagesInSingleBuffer() throws {\n        let mockSocket = try MockNetlinkSocket()\n        mockSocket.pid = 0x8765_4321\n\n        // Lookup all interfaces, with multiple messages packed into a single buffer.\n        // This tests the fix for parsing multiple netlink messages that arrive in one recv() call.\n        let expectedLookupRequest =\n            \"28000000120001030000000021436587\"  // Netlink header (16 B)\n            + \"110000000000000001000000ffffffff\"  // struct ifinfomsg (16 B)\n            + \"08001d0009000000\"  // RT attr: IFLA_EXT_MASK (8 B)\n\n        // Pack three messages into a single response buffer:\n        //\n        // Message 1: loopback interface with one attribute\n        let msg1 =\n            \"28000000100002000000000021436587\"  // Netlink header (16 B), len=40\n            + \"00000403010000004900010000000000\"  // struct ifinfomsg (16 B)\n            + \"070003006c6f0000\"  // IFLA_IFNAME \"lo\" (8 B, padded)\n\n        // Message 2: tunl0 interface with one attribute\n        let msg2 =\n            \"2c000000100002000000000021436587\"  // Netlink header (16 B), len=44\n            + \"00000003040000008000000000000000\"  // struct ifinfomsg (16 B)\n            + \"0a00030074756e6c30000000\"  // IFLA_IFNAME \"tunl0\" attr (12 B, padded)\n\n        // Message 3: eth0 interface with two attributes\n        let msg3 =\n            \"34000000100002000000000021436587\"  // Netlink header (16 B), len=52\n            + \"00000100020000004300010000000000\"  // struct ifinfomsg (16 B)\n            + \"090003006574683000000000\"  // IFLA_IFNAME \"eth0\" attr (12 B)\n            + \"08000d00e8030000\"  // IFLA_MTU = 1000 attr (8 B)\n\n        // Combine all three messages into a single buffer\n        mockSocket.responses.append([UInt8](hex: msg1 + msg2 + msg3))\n\n        // Final NLMSG_DONE message in separate buffer\n        mockSocket.responses.append(\n            [UInt8](\n                hex:\n                    \"14000000030002000000000021436587\"  // Netlink header (16 B) – NLMSG_DONE\n                    + \"00000000\"  // 4-byte payload\n            )\n        )\n\n        let session = NetlinkSession(socket: mockSocket)\n        let links = try session.linkGet()\n\n        #expect(mockSocket.requests.count == 1)\n        #expect(mockSocket.responseIndex == 2)\n        mockSocket.requests[0][8..<12] = [0, 0, 0, 0]\n        #expect(expectedLookupRequest == mockSocket.requests[0].hexEncodedString())\n\n        // Verify we got all three interfaces\n        try #require(links.count == 3)\n\n        // Verify loopback interface\n        #expect(links[0].interfaceIndex == 1)\n        try #require(links[0].attrDatas.count == 1)\n        #expect(links[0].attrDatas[0].attribute.type == 0x0003)\n        #expect(links[0].attrDatas[0].attribute.len == 0x0007)\n        #expect(links[0].attrDatas[0].data == [0x6c, 0x6f, 0x00])\n\n        // Verify tunl0 interface\n        #expect(links[1].interfaceIndex == 4)\n        try #require(links[1].attrDatas.count == 1)\n        #expect(links[1].attrDatas[0].attribute.type == 0x0003)\n        #expect(links[1].attrDatas[0].attribute.len == 0x000a)\n        #expect(links[1].attrDatas[0].data == [0x74, 0x75, 0x6e, 0x6c, 0x30, 0x00])\n\n        // Verify eth0 interface\n        #expect(links[2].interfaceIndex == 2)\n        try #require(links[2].attrDatas.count == 2)\n        #expect(links[2].attrDatas[0].attribute.type == 0x0003)\n        #expect(links[2].attrDatas[0].attribute.len == 0x0009)\n        #expect(links[2].attrDatas[0].data == [0x65, 0x74, 0x68, 0x30, 0x00])\n        #expect(links[2].attrDatas[1].attribute.type == 0x000d)\n        #expect(links[2].attrDatas[1].attribute.len == 0x0008)\n        #expect(links[2].attrDatas[1].data == [0xe8, 0x03, 0x00, 0x00])\n    }\n}\n\nextension Array where Element == UInt8 {\n    /// Initializes `[UInt8]` from an even-length hex string\n    init(hex: String) {\n        self = stride(from: 0, to: hex.count, by: 2).compactMap {\n            UInt8(\n                hex[hex.index(hex.startIndex, offsetBy: $0)...]\n                    .prefix(2), radix: 16)\n        }\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationNetlinkTests/TypesTest.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//\n\nimport Testing\n\n@testable import ContainerizationNetlink\n\nstruct TypesTest {\n    @Test func testNetlinkMessageHeader() throws {\n        let expectedValue = NetlinkMessageHeader(\n            len: 0x1234_5678, type: 0x9abc, flags: 0xdef0, seq: 0x1122_3344, pid: 0x5566_7788)\n        let expectedBuffer: [UInt8] = [\n            0x78, 0x56, 0x34, 0x12,\n            0xbc, 0x9a, 0xf0, 0xde,\n            0x44, 0x33, 0x22, 0x11,\n            0x88, 0x77, 0x66, 0x55,\n        ]\n        var buffer = [UInt8](repeating: 0, count: NetlinkMessageHeader.size)\n        let offset = try expectedValue.appendBuffer(&buffer, offset: 0)\n        #expect(NetlinkMessageHeader.size == offset)\n        #expect(expectedBuffer == buffer)\n        guard let (offset, value) = buffer.copyOut(as: NetlinkMessageHeader.self) else {\n            #expect(Bool(false), \"could not bind value to buffer\")\n            return\n\n        }\n\n        #expect(offset == NetlinkMessageHeader.size)\n        #expect(expectedValue == value)\n    }\n\n    @Test func testInterfaceInfo() throws {\n        let expectedValue = InterfaceInfo(\n            family: UInt8(AddressFamily.AF_NETLINK), type: 0x1234, index: 0x1234_5678, flags: 0x9abc_def0,\n            change: 0x0fed_cba9\n        )\n        let expectedBuffer: [UInt8] = [\n            0x10, 0x00, 0x34, 0x12,\n            0x78, 0x56, 0x34, 0x12,\n            0xf0, 0xde, 0xbc, 0x9a,\n            0xa9, 0xcb, 0xed, 0x0f,\n        ]\n        var buffer = [UInt8](repeating: 0, count: InterfaceInfo.size)\n        let offset = try expectedValue.appendBuffer(&buffer, offset: 0)\n        #expect(InterfaceInfo.size == offset)\n        #expect(expectedBuffer == buffer)\n        guard let (offset, value) = buffer.copyOut(as: InterfaceInfo.self) else {\n            #expect(Bool(false), \"could not bind value to buffer\")\n            return\n\n        }\n\n        #expect(offset == InterfaceInfo.size)\n        #expect(expectedValue == value)\n    }\n\n    @Test func testAddressInfo() throws {\n        let expectedValue = AddressInfo(\n            family: UInt8(AddressFamily.AF_INET), prefixLength: 24, flags: 0x5a, scope: 0xa5, index: 0xdead_beef)\n        let expectedBuffer: [UInt8] = [\n            0x02, 0x18, 0x5a, 0xa5,\n            0xef, 0xbe, 0xad, 0xde,\n        ]\n        var buffer = [UInt8](repeating: 0, count: AddressInfo.size)\n        let offset = try expectedValue.appendBuffer(&buffer, offset: 0)\n        #expect(AddressInfo.size == offset)\n        #expect(expectedBuffer == buffer)\n        guard let (offset, value) = buffer.copyOut(as: AddressInfo.self) else {\n            #expect(Bool(false), \"could not bind value to buffer\")\n            return\n\n        }\n\n        #expect(offset == AddressInfo.size)\n        #expect(expectedValue == value)\n    }\n\n    @Test func testRTAttribute() throws {\n        let expectedValue = RTAttribute(len: 0x1234, type: 0x5678)\n        let expectedBuffer: [UInt8] = [\n            0x34, 0x12, 0x78, 0x56,\n        ]\n        var buffer = [UInt8](repeating: 0, count: RTAttribute.size)\n        let offset = try expectedValue.appendBuffer(&buffer, offset: 0)\n        #expect(RTAttribute.size == offset)\n        #expect(expectedBuffer == buffer)\n        guard let (offset, value) = buffer.copyOut(as: RTAttribute.self) else {\n            #expect(Bool(false), \"could not bind value to buffer\")\n            return\n\n        }\n\n        #expect(offset == RTAttribute.size)\n        #expect(expectedValue == value)\n    }\n\n    @Test func testSockaddrNetlink() throws {\n        let expectedValue = SockaddrNetlink(family: 16, pid: 0x1234_5678, groups: 0x9abc_def0)\n        let expectedBuffer: [UInt8] = [\n            0x10, 0x00, 0x00, 0x00,\n            0x78, 0x56, 0x34, 0x12,\n            0xf0, 0xde, 0xbc, 0x9a,\n        ]\n        var buffer = [UInt8](repeating: 0, count: SockaddrNetlink.size)\n        let offset = try expectedValue.appendBuffer(&buffer, offset: 0)\n        #expect(SockaddrNetlink.size == offset)\n        #expect(expectedBuffer == buffer)\n\n        var unmarshaledValue = SockaddrNetlink()\n        let bindOffset = try unmarshaledValue.bindBuffer(&buffer, offset: 0)\n        #expect(bindOffset == SockaddrNetlink.size)\n        #expect(expectedValue == unmarshaledValue)\n    }\n\n    @Test func testRouteInfo() throws {\n        let expectedValue = RouteInfo(\n            family: UInt8(AddressFamily.AF_INET),\n            dstLen: 24,\n            srcLen: 0,\n            tos: 0,\n            table: RouteTable.MAIN,\n            proto: RouteProtocol.KERNEL,\n            scope: RouteScope.LINK,\n            type: RouteType.UNICAST,\n            flags: 0xdead_beef\n        )\n        let expectedBuffer: [UInt8] = [\n            0x02, 0x18, 0x00, 0x00,\n            0xfe, 0x02, 0xfd, 0x01,\n            0xef, 0xbe, 0xad, 0xde,\n        ]\n        var buffer = [UInt8](repeating: 0, count: RouteInfo.size)\n        let offset = try expectedValue.appendBuffer(&buffer, offset: 0)\n        #expect(RouteInfo.size == offset)\n        #expect(expectedBuffer == buffer)\n\n        var unmarshaledValue = RouteInfo(\n            dstLen: 0, srcLen: 0, tos: 0, table: 0, proto: 0, scope: 0, type: 0, flags: 0)\n        let bindOffset = try unmarshaledValue.bindBuffer(&buffer, offset: 0)\n        #expect(bindOffset == RouteInfo.size)\n        #expect(expectedValue == unmarshaledValue)\n    }\n\n    @Test func testLinkStatistics64() throws {\n        var expectedValue = LinkStatistics64()\n        expectedValue.rxPackets = 0x0102_0304_0506_0708\n        expectedValue.txPackets = 0x090a_0b0c_0d0e_0f10\n        expectedValue.rxBytes = 0x1112_1314_1516_1718\n        expectedValue.txBytes = 0x191a_1b1c_1d1e_1f20\n        expectedValue.rxErrors = 0x2122_2324_2526_2728\n        expectedValue.txErrors = 0x292a_2b2c_2d2e_2f30\n        expectedValue.rxDropped = 0x3132_3334_3536_3738\n        expectedValue.txDropped = 0x393a_3b3c_3d3e_3f40\n        expectedValue.multicast = 0x4142_4344_4546_4748\n        expectedValue.collisions = 0x494a_4b4c_4d4e_4f50\n        expectedValue.rxLengthErrors = 0x5152_5354_5556_5758\n        expectedValue.rxOverErrors = 0x595a_5b5c_5d5e_5f60\n        expectedValue.rxCrcErrors = 0x6162_6364_6566_6768\n        expectedValue.rxFrameErrors = 0x696a_6b6c_6d6e_6f70\n        expectedValue.rxFifoErrors = 0x7172_7374_7576_7778\n        expectedValue.rxMissedErrors = 0x797a_7b7c_7d7e_7f80\n        expectedValue.txAbortedErrors = 0x8182_8384_8586_8788\n        expectedValue.txCarrierErrors = 0x898a_8b8c_8d8e_8f90\n        expectedValue.txFifoErrors = 0x9192_9394_9596_9798\n        expectedValue.txHeartbeatErrors = 0x999a_9b9c_9d9e_9fa0\n        expectedValue.txWindowErrors = 0xa1a2_a3a4_a5a6_a7a8\n        expectedValue.rxCompressed = 0xa9aa_abac_adae_afb0\n        expectedValue.txCompressed = 0xb1b2_b3b4_b5b6_b7b8\n\n        let expectedBuffer: [UInt8] = [\n            0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,\n            0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09,\n            0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11,\n            0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19,\n            0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21,\n            0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29,\n            0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31,\n            0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39,\n            0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41,\n            0x50, 0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49,\n            0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51,\n            0x60, 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59,\n            0x68, 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61,\n            0x70, 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69,\n            0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71,\n            0x80, 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79,\n            0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81,\n            0x90, 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89,\n            0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91,\n            0xa0, 0x9f, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99,\n            0xa8, 0xa7, 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1,\n            0xb0, 0xaf, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9,\n            0xb8, 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1,\n        ]\n\n        var buffer = [UInt8](repeating: 0, count: LinkStatistics64.size)\n        let offset = try expectedValue.appendBuffer(&buffer, offset: 0)\n        #expect(LinkStatistics64.size == offset)\n        #expect(expectedBuffer == buffer)\n\n        var unmarshaledValue = LinkStatistics64()\n        let bindOffset = try unmarshaledValue.bindBuffer(&buffer, offset: 0)\n        #expect(bindOffset == LinkStatistics64.size)\n        #expect(expectedValue == unmarshaledValue)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationOCITests/AuthChallengeTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationOCI\n\nstruct AuthChallengeTests {\n    internal struct TestCase: Sendable {\n        let input: String\n        let expected: AuthenticateChallenge\n    }\n\n    private static let testCases: [TestCase] = [\n        .init(\n            input: \"\"\"\n                Bearer realm=\"https://domain.io/token\",service=\"domain.io\",scope=\"repository:user/image:pull\"\n                \"\"\",\n            expected: .init(type: \"Bearer\", realm: \"https://domain.io/token\", service: \"domain.io\", scope: \"repository:user/image:pull\", error: nil)),\n        .init(\n            input: \"\"\"\n                Bearer realm=\"https://foo-bar-registry.com/auth\",service=\"Awesome Registry\"\n                \"\"\",\n            expected: .init(type: \"Bearer\", realm: \"https://foo-bar-registry.com/auth\", service: \"Awesome Registry\", scope: nil, error: nil)),\n        .init(\n            input: \"\"\"\n                Bearer realm=\"users.example.com\", scope=\"create delete\"\n                \"\"\",\n            expected: .init(type: \"Bearer\", realm: \"users.example.com\", service: nil, scope: \"create delete\", error: nil)),\n        .init(\n            input: \"\"\"\n                Bearer realm=\"https://auth.server.io/token\",service=\"registry.server.io\"\n                \"\"\",\n            expected: .init(type: \"Bearer\", realm: \"https://auth.server.io/token\", service: \"registry.server.io\", scope: nil, error: nil)),\n\n    ]\n\n    @Test(arguments: testCases)\n    func parseAuthHeader(testCase: TestCase) throws {\n        let challenges = RegistryClient.parseWWWAuthenticateHeaders(headers: [testCase.input])\n        #expect(challenges.count == 1)\n        #expect(challenges[0] == testCase.expected)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationOCITests/OCIImageTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationOCI\n\nstruct OCITests {\n    @Test func config() {\n        let config = ContainerizationOCI.ImageConfig()\n        let rootfs = ContainerizationOCI.Rootfs(type: \"foo\", diffIDs: [\"diff1\", \"diff2\"])\n        let history = ContainerizationOCI.History()\n\n        let image = ContainerizationOCI.Image(architecture: \"arm64\", os: \"linux\", config: config, rootfs: rootfs, history: [history])\n        #expect(image.rootfs.type == \"foo\")\n    }\n\n    @Test func descriptor() {\n        let platform = ContainerizationOCI.Platform(arch: \"arm64\", os: \"linux\")\n        let descriptor = ContainerizationOCI.Descriptor(mediaType: MediaTypes.descriptor, digest: \"123\", size: 0, platform: platform)\n\n        #expect(descriptor.platform?.architecture == \"arm64\")\n        #expect(descriptor.platform?.os == \"linux\")\n        #expect(descriptor.artifactType == nil)\n    }\n\n    @Test func descriptorWithArtifactType() throws {\n        let testArtifactType = \"application/vnd.example.test.v1+json\"\n        let descriptor = ContainerizationOCI.Descriptor(\n            mediaType: MediaTypes.imageManifest,\n            digest: \"sha256:abc123\",\n            size: 1234,\n            artifactType: testArtifactType\n        )\n        #expect(descriptor.artifactType == testArtifactType)\n\n        let data = try JSONEncoder().encode(descriptor)\n        let decoded = try JSONDecoder().decode(ContainerizationOCI.Descriptor.self, from: data)\n        #expect(decoded.artifactType == testArtifactType)\n    }\n\n    @Test func descriptorWithoutArtifactTypeDecodesAsNil() throws {\n        let json = \"\"\"\n                {\"mediaType\":\"application/vnd.oci.descriptor.v1+json\",\"digest\":\"sha256:abc\",\"size\":0}\n            \"\"\"\n        let decoded = try JSONDecoder().decode(ContainerizationOCI.Descriptor.self, from: json.data(using: .utf8)!)\n        #expect(decoded.artifactType == nil)\n    }\n\n    @Test func index() {\n        var descriptors: [ContainerizationOCI.Descriptor] = []\n        for i in 0..<5 {\n            let descriptor = ContainerizationOCI.Descriptor(mediaType: MediaTypes.descriptor, digest: \"\\(i)\", size: Int64(i))\n            descriptors.append(descriptor)\n        }\n\n        let index = ContainerizationOCI.Index(schemaVersion: 1, manifests: descriptors)\n        #expect(index.manifests.count == 5)\n        #expect(index.subject == nil)\n        #expect(index.artifactType == nil)\n    }\n\n    @Test func indexWithSubjectAndArtifactType() throws {\n        let testArtifactType = \"application/vnd.example.test.v1+json\"\n        let subject = ContainerizationOCI.Descriptor(mediaType: MediaTypes.imageManifest, digest: \"sha256:subject\", size: 512)\n        let index = ContainerizationOCI.Index(\n            schemaVersion: 2,\n            manifests: [],\n            subject: subject,\n            artifactType: testArtifactType\n        )\n        #expect(index.subject?.digest == \"sha256:subject\")\n        #expect(index.artifactType == testArtifactType)\n\n        let data = try JSONEncoder().encode(index)\n        let decoded = try JSONDecoder().decode(ContainerizationOCI.Index.self, from: data)\n        #expect(decoded.subject?.digest == \"sha256:subject\")\n        #expect(decoded.artifactType == testArtifactType)\n    }\n\n    @Test func indexDecodesWithoutNewFields() throws {\n        let json = \"\"\"\n                {\"schemaVersion\":2,\"manifests\":[{\"mediaType\":\"application/vnd.oci.descriptor.v1+json\",\"digest\":\"sha256:abc\",\"size\":10}]}\n            \"\"\"\n        let decoded = try JSONDecoder().decode(ContainerizationOCI.Index.self, from: json.data(using: .utf8)!)\n        #expect(decoded.schemaVersion == 2)\n        #expect(decoded.manifests.count == 1)\n        #expect(decoded.subject == nil)\n        #expect(decoded.artifactType == nil)\n    }\n\n    @Test func manifests() {\n        var descriptors: [ContainerizationOCI.Descriptor] = []\n        for i in 0..<5 {\n            let descriptor = ContainerizationOCI.Descriptor(mediaType: MediaTypes.descriptor, digest: \"\\(i)\", size: Int64(i))\n            descriptors.append(descriptor)\n        }\n\n        let config = ContainerizationOCI.Descriptor(mediaType: MediaTypes.descriptor, digest: \"123\", size: 0)\n\n        let manifest = ContainerizationOCI.Manifest(schemaVersion: 1, config: config, layers: descriptors)\n        #expect(manifest.config.digest == \"123\")\n        #expect(manifest.layers.count == 5)\n        #expect(manifest.subject == nil)\n        #expect(manifest.artifactType == nil)\n    }\n\n    @Test func manifestWithSubjectAndArtifactType() throws {\n        let testArtifactType = \"application/vnd.example.test.v1+json\"\n        let config = ContainerizationOCI.Descriptor(mediaType: MediaTypes.emptyJSON, digest: \"sha256:empty\", size: 2)\n        let subject = ContainerizationOCI.Descriptor(mediaType: MediaTypes.imageManifest, digest: \"sha256:target\", size: 1234)\n        let layer = ContainerizationOCI.Descriptor(\n            mediaType: testArtifactType,\n            digest: \"sha256:meta\",\n            size: 89,\n            annotations: [\"org.opencontainers.image.title\": \"metadata.json\"]\n        )\n\n        let manifest = ContainerizationOCI.Manifest(\n            config: config,\n            layers: [layer],\n            subject: subject,\n            artifactType: testArtifactType\n        )\n        #expect(manifest.subject?.digest == \"sha256:target\")\n        #expect(manifest.artifactType == testArtifactType)\n        #expect(manifest.layers[0].annotations?[\"org.opencontainers.image.title\"] == \"metadata.json\")\n\n        let data = try JSONEncoder().encode(manifest)\n        let decoded = try JSONDecoder().decode(ContainerizationOCI.Manifest.self, from: data)\n        #expect(decoded.subject?.digest == \"sha256:target\")\n        #expect(decoded.artifactType == testArtifactType)\n    }\n\n    @Test func manifestDecodesWithoutNewFields() throws {\n        let json = \"\"\"\n                {\n                    \"schemaVersion\": 2,\n                    \"config\": {\"mediaType\":\"application/vnd.oci.empty.v1+json\",\"digest\":\"sha256:abc\",\"size\":2},\n                    \"layers\": []\n                }\n            \"\"\"\n        let decoded = try JSONDecoder().decode(ContainerizationOCI.Manifest.self, from: json.data(using: .utf8)!)\n        #expect(decoded.schemaVersion == 2)\n        #expect(decoded.subject == nil)\n        #expect(decoded.artifactType == nil)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationOCITests/OCIPlatformTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//\n\nimport Testing\n\n@testable import ContainerizationOCI\n\nstruct OCIPlatformTests {\n    @Test func identicalPlatforms() {\n        let amd64lhs = Platform(arch: \"amd64\", os: \"linux\")\n        let amd64rhs = Platform(arch: \"amd64\", os: \"linux\")\n        #expect(amd64lhs == amd64rhs, \"amd64 platforms should be equal\")\n\n        let arm64lhs = Platform(arch: \"arm64\", os: \"linux\")\n        let arm64rhs = Platform(arch: \"arm64\", os: \"linux\")\n        #expect(arm64lhs == arm64rhs, \"arm64 platforms should be equal\")\n    }\n\n    @Test func differentOS() {\n        let lhs = Platform(arch: \"arm64\", os: \"linux\")\n        let rhs = Platform(arch: \"arm64\", os: \"darwin\")\n        #expect(lhs != rhs, \"Different OS should not be equal\")\n    }\n\n    @Test func differentArch() {\n        let lhs = Platform(arch: \"amd64\", os: \"linux\")\n        let rhs = Platform(arch: \"arm64\", os: \"linux\")\n        #expect(lhs != rhs, \"Different arch should not be equal\")\n    }\n\n    @Test func arm64_sameVariant() {\n        let lhs = Platform(arch: \"arm64\", os: \"linux\", variant: \"v8\")\n        let rhs = Platform(arch: \"arm64\", os: \"linux\", variant: \"v8\")\n        #expect(lhs == rhs, \"Both OS arm64, same arch, same variant => equal\")\n    }\n\n    @Test func arm64_nilAndV8() {\n        let lhs = Platform(arch: \"arm64\", os: \"linux\", variant: nil)\n        let rhs = Platform(arch: \"arm64\", os: \"linux\", variant: \"v8\")\n        #expect(lhs == rhs, \"One variant nil and other v8 => equal under special arm64 rule\")\n    }\n\n    @Test func arm64_nilAndV7() {\n        let lhs = Platform(arch: \"arm64\", os: \"linux\", variant: nil)\n        let rhs = Platform(arch: \"arm64\", os: \"linux\", variant: \"v7\")\n        #expect(lhs != rhs, \"nil vs v7 is not covered by the special rule => not equal\")\n    }\n\n    @Test func arm64_bothNil() {\n        let lhs = Platform(arch: \"arm64\", os: \"linux\", variant: nil)\n        let rhs = Platform(arch: \"arm64\", os: \"linux\", variant: nil)\n        #expect(lhs == rhs, \"Both nil variants => variantEqual is true => overall equal\")\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationOCITests/OCISpecTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationOCI\n\nstruct OCISpecTests {\n    @Test func minimalSpecDecode() throws {\n        let version = \"1.2.3\"\n        let minSpec =\n            \"\"\"\n                {\n                    \"ociVersion\": \"\\(version)\"\n                }\n            \"\"\"\n\n        guard let data = minSpec.data(using: .utf8) else {\n            Issue.record(\"test spec is not valid: \\(minSpec)\")\n            return\n        }\n\n        let decodedSpec = try JSONDecoder().decode(ContainerizationOCI.Spec.self, from: data)\n        #expect(decodedSpec.version == version)\n        #expect(decodedSpec.hooks == nil)\n        #expect(decodedSpec.process == nil)\n        #expect(decodedSpec.hostname == \"\")\n        #expect(decodedSpec.domainname == \"\")\n        #expect(decodedSpec.mounts.isEmpty)\n        #expect(decodedSpec.annotations == nil)\n        #expect(decodedSpec.root == nil)\n        #expect(decodedSpec.linux == nil)\n    }\n\n    @Test func minimalProcessSpecDecode() throws {\n        let cwd = \"/test\"\n        let minProcessSpec =\n            \"\"\"\n                {\n                    \"cwd\": \"\\(cwd)\",\n                    \"user\": {\n                        \"uid\": 10,\n                        \"gid\": 11\n                    }\n                }\n            \"\"\"\n\n        guard let data = minProcessSpec.data(using: .utf8) else {\n            Issue.record(\"test process spec is not valid: \\(minProcessSpec)\")\n            return\n        }\n\n        let decodedSpec = try JSONDecoder().decode(ContainerizationOCI.Process.self, from: data)\n        #expect(decodedSpec.cwd == cwd)\n        #expect(decodedSpec.env.isEmpty)\n        #expect(decodedSpec.consoleSize == nil)\n        #expect(decodedSpec.selinuxLabel == \"\")\n        #expect(decodedSpec.noNewPrivileges == false)\n        #expect(decodedSpec.commandLine == \"\")\n        #expect(decodedSpec.oomScoreAdj == nil)\n        #expect(decodedSpec.capabilities == nil)\n        #expect(decodedSpec.apparmorProfile == \"\")\n        #expect(decodedSpec.user.uid == 10)\n        #expect(decodedSpec.user.gid == 11)\n        #expect(decodedSpec.rlimits.isEmpty)\n        #expect(decodedSpec.terminal == false)\n    }\n\n    @Test func minimalUserSpecDecode() throws {\n        let minUserSpec =\n            \"\"\"\n                {\n                    \"uid\": 10,\n                    \"gid\": 11\n                }\n            \"\"\"\n\n        guard let data = minUserSpec.data(using: .utf8) else {\n            Issue.record(\"test user spec is not valid: \\(minUserSpec)\")\n            return\n        }\n\n        let decodedSpec = try JSONDecoder().decode(ContainerizationOCI.User.self, from: data)\n        #expect(decodedSpec.uid == 10)\n        #expect(decodedSpec.gid == 11)\n        #expect(decodedSpec.umask == nil)\n        #expect(decodedSpec.additionalGids.isEmpty)\n        #expect(decodedSpec.username == \"\")\n    }\n\n    @Test func minimalRootSpecDecode() throws {\n        let path = \"/testpath\"\n        let minRootSpec =\n            \"\"\"\n                {\n                    \"path\": \"\\(path)\"\n                }\n            \"\"\"\n\n        guard let data = minRootSpec.data(using: .utf8) else {\n            Issue.record(\"test root spec is not valid: \\(minRootSpec)\")\n            return\n        }\n\n        let decodedSpec = try JSONDecoder().decode(ContainerizationOCI.Root.self, from: data)\n        #expect(decodedSpec.path == path)\n        #expect(decodedSpec.readonly == false)\n    }\n\n    @Test func minimalMountSpecDecode() throws {\n        let destination = \"/testdest\"\n        let minMountSpec =\n            \"\"\"\n                {\n                    \"destination\": \"\\(destination)\"\n                }\n            \"\"\"\n\n        guard let data = minMountSpec.data(using: .utf8) else {\n            Issue.record(\"test mount spec is not valid: \\(minMountSpec)\")\n            return\n        }\n\n        let decodedSpec = try JSONDecoder().decode(ContainerizationOCI.Mount.self, from: data)\n        #expect(decodedSpec.type == \"\")\n        #expect(decodedSpec.source == \"\")\n        #expect(decodedSpec.destination == destination)\n        #expect(decodedSpec.options.isEmpty)\n        #expect(decodedSpec.uidMappings == nil)\n        #expect(decodedSpec.gidMappings == nil)\n    }\n\n    @Test func minimalCapabilitiesDecode() throws {\n        let minCapabilitiesSpec =\n            \"\"\"\n                {\n                    \"ociVersion\": \"1.1.0\",\n                    \"capabilities\": {\n                        \"permitted\": [\n                            \"CAP_SYS_ADMIN\"\n                        ]\n                    },\n                    \"linux\": {}\n                }\n            \"\"\"\n\n        guard let data = minCapabilitiesSpec.data(using: .utf8) else {\n            Issue.record(\"test capabilities spec is not valid: \\(minCapabilitiesSpec)\")\n            return\n        }\n\n        let _ = try JSONDecoder().decode(ContainerizationOCI.Spec.self, from: data)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationOCITests/ReferenceTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n// swiftlint:disable force_cast large_tuple\n\nimport ContainerizationError\nimport Foundation\nimport Testing\n\n@testable import ContainerizationOCI\n\n@Suite(\"Reference Parse Tests\")\nstruct ReferenceParseTests {\n    internal struct ReferenceParseTestCase: Sendable {\n        let input: String\n        let domain: String?\n        let path: String\n        let tag: String?\n        let digest: String?\n        init(input: String, domain: String? = nil, path: String, tag: String? = nil, digest: String? = nil) {\n            self.input = input\n            self.domain = domain\n            self.path = path\n            self.tag = tag\n            self.digest = digest\n        }\n    }\n\n    @Test(arguments: [\n        ReferenceParseTestCase(input: \"tensorflow/tensorflow\", path: \"tensorflow/tensorflow\"),\n        ReferenceParseTestCase(input: \"debian\", path: \"debian\"),\n        ReferenceParseTestCase(input: \"repo_with_underscore\", path: \"repo_with_underscore\"),\n        ReferenceParseTestCase(input: \"swift5.10:alpine\", path: \"swift5.10\", tag: \"alpine\"),\n        ReferenceParseTestCase(input: \"registry.com.with.port:5000/no_tag\", domain: \"registry.com.with.port:5000\", path: \"no_tag\"),\n        ReferenceParseTestCase(input: \"registry.com.with.port:5000/name/foo/bar:tag23\", domain: \"registry.com.with.port:5000\", path: \"name/foo/bar\", tag: \"tag23\"),\n        ReferenceParseTestCase(input: \"some-repo-with-dashes/name\", path: \"some-repo-with-dashes/name\"),\n        ReferenceParseTestCase(input: \"domain.with-dashes/cool-image:foo\", domain: \"domain.with-dashes\", path: \"cool-image\", tag: \"foo\"),\n        ReferenceParseTestCase(input: \"localhost:8080/123:latest\", domain: \"localhost:8080\", path: \"123\", tag: \"latest\"),\n        ReferenceParseTestCase(\n            input: \"localhost/123@sha256:\\(String(repeating: \"a\", count: 64))\", domain: \"localhost\", path: \"123\", digest: \"sha256:\\(String(repeating: \"a\", count: 64))\"),\n        ReferenceParseTestCase(\n            input: \"registry.com.with.port:1254/foo/bar/baz@sha256:\\(String(repeating: \"abcd\", count: 16))\", domain: \"registry.com.with.port:1254\", path: \"foo/bar/baz\",\n            digest: \"sha256:\\(String(repeating: \"abcd\", count: 16))\"),\n        ReferenceParseTestCase(input: \"192.168.1.1:5544/local/swift:6.0\", domain: \"192.168.1.1:5544\", path: \"local/swift\", tag: \"6.0\"),\n        ReferenceParseTestCase(input: \"[abc12::4]:5683/swift\", domain: \"[abc12::4]:5683\", path: \"swift\"),\n        // Verify names longer than 127 characters are accepted (OCI spec allows up to 255).\n        ReferenceParseTestCase(\n            input: \"reg.io/\\(String(repeating: \"a\", count: 121))\",\n            domain: \"reg.io\",\n            path: String(repeating: \"a\", count: 121)),\n        // Verify a name of exactly 255 characters (the OCI spec maximum) is accepted.\n        ReferenceParseTestCase(\n            input: \"registry.example.com/\\(String(repeating: \"a\", count: 234))\",\n            domain: \"registry.example.com\",\n            path: String(repeating: \"a\", count: 234)),\n    ])\n    func validReferenceParse(testCase: ReferenceParseTestCase) async throws {\n        #expect(throws: Never.self) {\n            let parsed = try Reference.parse(testCase.input)\n            #expect(parsed.path == testCase.path)\n            #expect(parsed.domain == testCase.domain)\n            #expect(parsed.digest == testCase.digest)\n            #expect(parsed.tag == testCase.tag)\n        }\n    }\n\n    @Test(arguments: [\n        \"localhost:8080\",\n        \"localhost/123@sha256:\\(String(repeating: \"a\", count: 200))\",\n        \"https://github.com/apple/containerization\",\n        \"\",\n        \"-testString\",\n        \"-testString/image\",\n        \"-testString.com/image/release\",\n        \"foo///bar\",\n        \"mostly.valid/image/but/Caps\",\n        \"[abc12::4]\",\n        \"[abc12::4]:abc12::4\",\n        \"[2001:db8:3:4::192.0.2.33]:5000/debian\",\n        \"1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a\",\n        // A name of 256 characters exceeds the OCI spec limit of 255 and must be rejected.\n        \"registry.example.com/\\(String(repeating: \"a\", count: 235))\",\n    ])\n    func invalidReferenceParse(input: String) async throws {\n        #expect(throws: ContainerizationError.self) {\n            try Reference.parse(input)\n        }\n    }\n\n    @Test(arguments: [\n        ReferenceParseTestCase(input: \"only_name\", path: \"only_name\", tag: \"latest\"),\n        ReferenceParseTestCase(input: \"docker.io/alpine\", domain: \"docker.io\", path: \"library/alpine\", tag: \"latest\"),\n        ReferenceParseTestCase(input: \"ghcr.io/myrepo/alpine\", domain: \"ghcr.io\", path: \"myrepo/alpine\", tag: \"latest\"),\n        ReferenceParseTestCase(input: \"name@sha256:\" + String(repeating: \"1\", count: 64), path: \"name\", digest: \"sha256:\" + String(repeating: \"1\", count: 64)),\n        ReferenceParseTestCase(input: \"registry-1.docker.io/testrepo/myname:v2\", domain: \"registry-1.docker.io\", path: \"testrepo/myname\", tag: \"v2\"),\n    ])\n    func testNormalize(testCase: ReferenceParseTestCase) throws {\n        let parsed = try Reference.parse(testCase.input)\n        parsed.normalize()\n        #expect(parsed.path == testCase.path)\n        #expect(parsed.domain == testCase.domain)\n        #expect(parsed.digest == testCase.digest)\n        #expect(parsed.tag == testCase.tag)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationOCITests/RegistryClientTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//\n\nimport ContainerizationError\nimport ContainerizationIO\nimport Crypto\nimport Foundation\nimport NIO\nimport Synchronization\nimport Testing\n\n@testable import ContainerizationOCI\n\nstruct OCIClientTests: ~Copyable {\n    private var contentPath: URL\n    private let fileManager = FileManager.default\n    private var encoder = JSONEncoder()\n\n    init() async throws {\n        let testDir = fileManager.uniqueTemporaryDirectory()\n        let contentPath = testDir.appendingPathComponent(\"content\")\n        try fileManager.createDirectory(at: contentPath, withIntermediateDirectories: true)\n        self.contentPath = contentPath\n\n        encoder.outputFormatting = .prettyPrinted\n    }\n\n    deinit {\n        try? fileManager.removeItem(at: contentPath)\n    }\n\n    private static var arch: String? {\n        var uts = utsname()\n        let result = uname(&uts)\n        guard result == EXIT_SUCCESS else {\n            return nil\n        }\n\n        let machine = Data(bytes: &uts.machine, count: 256)\n        guard let arch = String(bytes: machine, encoding: .utf8) else {\n            return nil\n        }\n\n        switch arch.lowercased().trimmingCharacters(in: .controlCharacters) {\n        case \"arm64\":\n            return \"arm64\"\n        default:\n            return \"amd64\"\n        }\n    }\n\n    @Test(.enabled(if: hasRegistryCredentials))\n    func fetchToken() async throws {\n        let client = RegistryClient(host: \"ghcr.io\", authentication: Self.authentication)\n        let request = TokenRequest(realm: \"https://ghcr.io/token\", service: \"ghcr.io\", clientId: \"tests\", scope: nil)\n        let response = try await client.fetchToken(request: request)\n        #expect(response.getToken() != nil)\n    }\n\n    @Test(arguments: [\n        \"registry-1.docker.io\",\n        \"public.ecr.aws\",\n        \"registry.k8s.io\",\n        \"mcr.microsoft.com\",\n    ])\n    func ping(host: String) async throws {\n        let client = RegistryClient(host: host)\n        try await client.ping()\n    }\n\n    @Test func pingWithInvalidCredentials() async throws {\n        let authentication = BasicAuthentication(username: \"foo\", password: \"bar\")\n        let client = RegistryClient(host: \"ghcr.io\", authentication: authentication)\n        let error = await #expect(throws: RegistryClient.Error.self) { try await client.ping() }\n        guard case .invalidStatus(_, let status, let reason) = error else {\n            throw error!\n        }\n        #expect(status == .unauthorized)\n        #expect(reason == \"access denied or wrong credentials\")\n    }\n\n    @Test(.enabled(if: hasRegistryCredentials))\n    func pingWithCredentials() async throws {\n        let client = RegistryClient(host: \"ghcr.io\", authentication: Self.authentication)\n        try await client.ping()\n    }\n\n    @Test func resolve() async throws {\n        let client = RegistryClient(host: \"ghcr.io\")\n        let descriptor = try await client.resolve(name: \"apple/containerization/dockermanifestimage\", tag: \"0.0.2\")\n        #expect(descriptor.mediaType == MediaTypes.dockerManifest)\n        #expect(descriptor.size != 0)\n        #expect(!descriptor.digest.isEmpty)\n    }\n\n    @Test func resolveSha() async throws {\n        let client = RegistryClient(host: \"ghcr.io\")\n        let descriptor = try await client.resolve(\n            name: \"apple/containerization/dockermanifestimage\", tag: \"sha256:c8d344d228b7d9a702a95227438ec0d71f953a9a483e28ffabc5704f70d2b61e\")\n        let namedDescriptor = try await client.resolve(name: \"apple/containerization/dockermanifestimage\", tag: \"0.0.2\")\n        #expect(descriptor == namedDescriptor)\n        #expect(descriptor.mediaType == MediaTypes.dockerManifest)\n        #expect(descriptor.size != 0)\n        #expect(!descriptor.digest.isEmpty)\n    }\n\n    @Test func fetchManifest() async throws {\n        let client = RegistryClient(host: \"ghcr.io\")\n        let descriptor = try await client.resolve(name: \"apple/containerization/dockermanifestimage\", tag: \"0.0.2\")\n        let manifest: Manifest = try await client.fetch(name: \"apple/containerization/dockermanifestimage\", descriptor: descriptor)\n        #expect(manifest.schemaVersion == 2)\n        #expect(manifest.layers.count == 1)\n    }\n\n    @Test func fetchManifestAsData() async throws {\n        let client = RegistryClient(host: \"ghcr.io\")\n        let descriptor = try await client.resolve(name: \"apple/containerization/dockermanifestimage\", tag: \"0.0.2\")\n        let manifestData = try await client.fetchData(name: \"apple/containerization/dockermanifestimage\", descriptor: descriptor)\n        let checksum = SHA256.hash(data: manifestData)\n        #expect(descriptor.digest == checksum.digest)\n    }\n\n    @Test func fetchConfig() async throws {\n        let client = RegistryClient(host: \"ghcr.io\")\n        let descriptor = try await client.resolve(name: \"apple/containerization/dockermanifestimage\", tag: \"0.0.2\")\n        let manifest: Manifest = try await client.fetch(name: \"apple/containerization/dockermanifestimage\", descriptor: descriptor)\n        let image: Image = try await client.fetch(name: \"apple/containerization/dockermanifestimage\", descriptor: manifest.config)\n        // This is an empty image -- check that the image label is present in the image config\n        #expect(image.config?.labels?[\"org.opencontainers.image.source\"] == \"https://github.com/apple/containerization\")\n        #expect(image.rootfs.diffIDs.count == 1)\n    }\n\n    @Test func fetchBlob() async throws {\n        let client = RegistryClient(host: \"ghcr.io\")\n        let descriptor = try await client.resolve(name: \"apple/containerization/dockermanifestimage\", tag: \"0.0.2\")\n        let manifest: Manifest = try await client.fetch(name: \"apple/containerization/dockermanifestimage\", descriptor: descriptor)\n        var called = false\n        var done = false\n        try await client.fetchBlob(name: \"apple/containerization/dockermanifestimage\", descriptor: manifest.layers.first!) { (expected, body) in\n            called = true\n            #expect(expected != 0)\n            var received = 0\n            for try await buffer in body {\n                received += buffer.readableBytes\n                if received == expected {\n                    done = true\n                }\n            }\n        }\n        #expect(called)\n        #expect(done)\n    }\n\n    @Test(.disabled(\"External users cannot push images, disable while we find a better solution\"))\n    func pushIndex() async throws {\n        let client = RegistryClient(host: \"ghcr.io\", authentication: Self.authentication)\n        let indexDescriptor = try await client.resolve(name: \"apple/containerization/emptyimage\", tag: \"0.0.1\")\n        let index: Index = try await client.fetch(name: \"apple/containerization/emptyimage\", descriptor: indexDescriptor)\n\n        let platform = Platform(arch: \"amd64\", os: \"linux\")\n\n        var manifestDescriptor: Descriptor?\n        for m in index.manifests where m.platform == platform {\n            manifestDescriptor = m\n            break\n        }\n\n        #expect(manifestDescriptor != nil)\n\n        let manifest: Manifest = try await client.fetch(name: \"apple/containerization/emptyimage\", descriptor: manifestDescriptor!)\n        let imgConfig: Image = try await client.fetch(name: \"apple/containerization/emptyimage\", descriptor: manifest.config)\n\n        let layer = try #require(manifest.layers.first)\n        let blobPath = contentPath.appendingPathComponent(layer.digest)\n        let outputStream = OutputStream(toFileAtPath: blobPath.path, append: false)\n        #expect(outputStream != nil)\n\n        try await outputStream!.withThrowingOpeningStream {\n            try await client.fetchBlob(name: \"apple/containerization/emptyimage\", descriptor: layer) { (expected, body) in\n                var received: Int64 = 0\n                for try await buffer in body {\n                    received += Int64(buffer.readableBytes)\n\n                    buffer.withUnsafeReadableBytes { pointer in\n                        let unsafeBufferPointer = pointer.bindMemory(to: UInt8.self)\n                        if let addr = unsafeBufferPointer.baseAddress {\n                            outputStream!.write(addr, maxLength: buffer.readableBytes)\n                        }\n                    }\n                }\n\n                #expect(received == expected)\n            }\n        }\n\n        let name = \"apple/test-images/image-push\"\n        let ref = \"latest\"\n\n        // Push the layer first.\n        do {\n            let content = try LocalContent(path: blobPath)\n            let generator = {\n                let stream = try ReadStream(url: content.path)\n                try stream.reset()\n                return stream.stream\n            }\n            try await client.push(name: name, ref: ref, descriptor: layer, streamGenerator: generator, progress: nil)\n        } catch let err as ContainerizationError {\n            guard err.code == .exists else {\n                throw err\n            }\n        }\n\n        // Push the image configuration.\n        var imgConfigDesc: Descriptor?\n        do {\n            imgConfigDesc = try await self.pushDescriptor(\n                client: client,\n                name: name,\n                ref: ref,\n                content: imgConfig,\n                baseDescriptor: manifest.config\n            )\n        } catch let err as ContainerizationError {\n            guard err.code != .exists else {\n                return\n            }\n            throw err\n        }\n\n        // Push the image manifest.\n        let newManifest = Manifest(\n            schemaVersion: manifest.schemaVersion,\n            mediaType: manifest.mediaType!,\n            config: imgConfigDesc!,\n            layers: manifest.layers,\n            annotations: manifest.annotations\n        )\n        let manifestDesc = try await self.pushDescriptor(\n            client: client,\n            name: name,\n            ref: ref,\n            content: newManifest,\n            baseDescriptor: manifestDescriptor!\n        )\n\n        // Push the index.\n        let newIndex = Index(\n            schemaVersion: index.schemaVersion,\n            mediaType: index.mediaType,\n            manifests: [manifestDesc],\n            annotations: index.annotations\n        )\n        try await self.pushDescriptor(\n            client: client,\n            name: name,\n            ref: ref,\n            content: newIndex,\n            baseDescriptor: indexDescriptor\n        )\n    }\n\n    @Test func resolveWithRetry() async throws {\n        let counter = Mutex(0)\n        let client = RegistryClient(\n            host: \"ghcr.io\",\n            retryOptions: RetryOptions(\n                maxRetries: 3,\n                retryInterval: 500_000_000,\n                shouldRetry: ({ response in\n                    if response.status == .notFound {\n                        counter.withLock { $0 += 1 }\n                        return true\n                    }\n                    return false\n                })\n            )\n        )\n        do {\n            _ = try await client.resolve(name: \"containerization/not-exists\", tag: \"foo\")\n        } catch {\n            #expect(counter.withLock { $0 } <= 3)\n        }\n    }\n\n    // MARK: private functions\n\n    static var hasRegistryCredentials: Bool {\n        authentication != nil\n    }\n\n    static var authentication: Authentication? {\n        let env = ProcessInfo.processInfo.environment\n        guard let password = env[\"REGISTRY_TOKEN\"],\n            let username = env[\"REGISTRY_USERNAME\"]\n        else {\n            return nil\n        }\n        return BasicAuthentication(username: username, password: password)\n    }\n\n    @discardableResult\n    private func pushDescriptor<T: Encodable>(\n        client: RegistryClient,\n        name: String,\n        ref: String,\n        content: T,\n        baseDescriptor: Descriptor\n    ) async throws -> Descriptor {\n        let encoded = try self.encoder.encode(content)\n        let digest = SHA256.hash(data: encoded)\n        let descriptor = Descriptor(\n            mediaType: baseDescriptor.mediaType,\n            digest: digest.digest,\n            size: Int64(encoded.count),\n            urls: baseDescriptor.urls,\n            annotations: baseDescriptor.annotations,\n            platform: baseDescriptor.platform\n        )\n        let generator = {\n            let stream = ReadStream(data: encoded)\n            try stream.reset()\n            return stream.stream\n        }\n\n        try await client.push(\n            name: name,\n            ref: ref,\n            descriptor: descriptor,\n            streamGenerator: generator,\n            progress: nil\n        )\n        return descriptor\n    }\n}\n\nextension OutputStream {\n    fileprivate func withThrowingOpeningStream(_ closure: () async throws -> Void) async throws {\n        self.open()\n        defer { self.close() }\n\n        try await closure()\n    }\n}\n\nextension SHA256.Digest {\n    fileprivate var digest: String {\n        let parts = self.description.split(separator: \": \")\n        return \"sha256:\\(parts[1])\"\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationOSTests/FileDescriptor+SecurePathTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport SystemPackage\nimport Testing\n\n@testable import ContainerizationOS\n\n#if canImport(Darwin)\nimport Darwin\nlet os_close = Darwin.close\n#elseif canImport(Musl)\nimport Musl\nlet os_close = Musl.close\n#elseif canImport(Glibc)\nimport Glibc\nlet os_close = Glibc.close\n#endif\n\nstruct FileDescriptorPathSecureTests {\n    @Test(\n        \"Test creation of stub file under directory successfully created by secure mkdir\",\n        arguments: [\n            // Case 1: Single component, no intermediates needed, default permissions\n            ([Entry](), FilePath(\"foo\"), nil as FilePermissions?, false),\n\n            // Case 2: Single component with explicit permissions\n            ([Entry](), FilePath(\"foo\"), FilePermissions(rawValue: 0o755), false),\n\n            // Case 3: Two components, parent exists, no intermediates\n            ([Entry.directory(path: \"foo\")], FilePath(\"foo/bar\"), nil as FilePermissions?, false),\n\n            // Case 4: Two components, parent missing, makeIntermediates true\n            ([Entry](), FilePath(\"foo/bar\"), nil as FilePermissions?, true),\n\n            // Case 5: Three components, makeIntermediates true, custom permissions\n            ([Entry](), FilePath(\"foo/bar/baz\"), FilePermissions(rawValue: 0o700), true),\n\n            // Case 6: Replace existing file with directory (single component)\n            ([Entry.regular(path: \"foo\")], FilePath(\"foo\"), nil as FilePermissions?, false),\n\n            // Case 7: Replace existing file with directory path (makeIntermediates true)\n            ([Entry.regular(path: \"foo\")], FilePath(\"foo/bar\"), nil as FilePermissions?, true),\n\n            // Case 8: Replace existing directory with new directory (should be idempotent)\n            ([Entry.directory(path: \"foo\")], FilePath(\"foo\"), nil as FilePermissions?, false),\n\n            // Case 9: Replace nested directory structure\n            (\n                [\n                    Entry.directory(path: \"foo/bar\"),\n                    Entry.regular(path: \"foo/bar/file.txt\"),\n                ], FilePath(\"foo/bar\"), nil as FilePermissions?, false\n            ),\n\n            // Case 10: Replace symlink with directory\n            ([Entry.symlink(target: \"target\", source: \"foo\")], FilePath(\"foo\"), nil as FilePermissions?, false),\n\n            // Case 11: Multi-level with some intermediates existing\n            ([Entry.directory(path: \"foo\")], FilePath(\"foo/bar/baz\"), nil as FilePermissions?, true),\n\n            // Case 12: Deep nesting with makeIntermediates\n            ([Entry](), FilePath(\"a/b/c/d/e\"), nil as FilePermissions?, true),\n        ]\n    )\n    func testMkdirSecureValid(entries: [Entry], relativePath: FilePath, permissions: FilePermissions?, makeIntermediates: Bool) async throws {\n        let rootPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: rootPath.string) }\n        try createEntries(rootPath: rootPath, entries: entries, permissions: permissions)\n        let rootFd = try FileDescriptor.open(rootPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        let stubFileName = \"stub.txt\"\n        let stubContent = Data(\"stub file content\".utf8)\n\n        try rootFd.mkdirSecure(relativePath, permissions: permissions, makeIntermediates: makeIntermediates) { dirFd in\n            // Create a stub file in the directory using openat\n            let fd = openat(\n                dirFd.rawValue,\n                stubFileName,\n                O_WRONLY | O_CREAT | O_TRUNC,\n                0o644\n            )\n            guard fd >= 0 else {\n                throw Errno(rawValue: errno)\n            }\n            defer { close(fd) }\n\n            try stubContent.withUnsafeBytes { buffer in\n                guard let baseAddress = buffer.baseAddress else { return }\n                let written = write(fd, baseAddress, buffer.count)\n                guard written == buffer.count else {\n                    throw Errno(rawValue: errno)\n                }\n            }\n        }\n\n        // Check stub file existence at expected location\n        let expectedStubPath = rootPath.appending(relativePath.string).appending(stubFileName)\n        #expect(FileManager.default.fileExists(atPath: expectedStubPath.string))\n\n        // Verify stub file content\n        let readContent = try Data(contentsOf: URL(fileURLWithPath: expectedStubPath.string))\n        #expect(readContent == stubContent)\n\n        // Check directory permissions if specified\n        if let permissions = permissions {\n            // Check each component of the path\n            let components = relativePath.components\n            var currentPath = \"\"\n            for (index, component) in components.enumerated() {\n                if index > 0 {\n                    currentPath += \"/\"\n                }\n                currentPath += component.string\n\n                let dirPath = rootPath.appending(currentPath)\n                let attrs = try FileManager.default.attributesOfItem(atPath: dirPath.string)\n                let posixPerms = attrs[.posixPermissions] as? NSNumber\n                // Mask to permission bits only (not file type bits)\n                let permMask: UInt16 = 0o777\n                let actualPerms = (posixPerms?.uint16Value ?? 0) & permMask\n                let expectedPerms = permissions.rawValue & permMask\n                #expect(\n                    actualPerms == expectedPerms,\n                    \"Directory '\\(currentPath)' has permissions 0o\\(String(actualPerms, radix: 8)) but expected 0o\\(String(expectedPerms, radix: 8))\")\n            }\n        }\n    }\n\n    @Test(\n        \"Test mkdirSecure error cases\",\n        arguments: [\n            // Case 1: Path starting with \"..\" should be rejected\n            (FilePath(\"../escape\"), false, SecurePathError.invalidRelativePath),\n\n            // Case 2: Path with \"..\" in middle that would escape\n            (FilePath(\"foo/../../escape\"), false, SecurePathError.invalidRelativePath),\n\n            // Case 3: Missing intermediate without makeIntermediates should fail\n            (FilePath(\"missing/intermediate/path\"), false, SecurePathError.invalidPathComponent),\n\n            // Case 4: Multiple .. that escape\n            (FilePath(\"a/b/../../../escape\"), false, SecurePathError.invalidRelativePath),\n        ]\n    )\n    func testMkdirSecureInvalid(relativePath: FilePath, makeIntermediates: Bool, expectedError: SecurePathError) async throws {\n        let rootPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: rootPath.string) }\n\n        let rootFd = try FileDescriptor.open(rootPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        // Attempt the operation and expect it to throw\n        #expect {\n            try rootFd.mkdirSecure(relativePath, makeIntermediates: makeIntermediates) { _ in }\n        } throws: { error in\n            guard let securePathError = error as? SecurePathError else {\n                return false\n            }\n            // Compare error cases\n            switch (securePathError, expectedError) {\n            case (.invalidRelativePath, .invalidRelativePath),\n                (.invalidPathComponent, .invalidPathComponent),\n                (.cannotFollowSymlink, .cannotFollowSymlink):\n                return true\n            case (.systemError(let op1, let err1), .systemError(let op2, let err2)):\n                return op1 == op2 && err1 == err2\n            default:\n                return false\n            }\n        }\n    }\n\n    @Test(\n        \"Test paths with .. that normalize to valid paths\",\n        arguments: [\n            // Paths with .. that should normalize and succeed\n            (\"./safe\", \"safe\"),  // Leading ./ normalizes to safe\n            (\"./a/./b\", \"a/b\"),  // Multiple ./ normalize away\n        ]\n    )\n    func testPathsWithDotNormalization(path: String, expectedNormalized: String) async throws {\n        let rootPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: rootPath.string) }\n\n        let rootFd = try FileDescriptor.open(rootPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        let stubFileName = \"stub.txt\"\n        let stubContent = Data(\"stub file content\".utf8)\n\n        try rootFd.mkdirSecure(FilePath(path), makeIntermediates: true) { dirFd in\n            // Create a stub file to verify we're in the right place\n            let fd = openat(\n                dirFd.rawValue,\n                stubFileName,\n                O_WRONLY | O_CREAT | O_TRUNC,\n                0o644\n            )\n            guard fd >= 0 else {\n                throw Errno(rawValue: errno)\n            }\n            defer { close(fd) }\n\n            try stubContent.withUnsafeBytes { buffer in\n                guard let baseAddress = buffer.baseAddress else { return }\n                let written = write(fd, baseAddress, buffer.count)\n                guard written == buffer.count else {\n                    throw Errno(rawValue: errno)\n                }\n            }\n        }\n\n        // Verify stub file exists at the normalized location\n        let expectedPath =\n            expectedNormalized.isEmpty\n            ? rootPath.appending(stubFileName)\n            : rootPath.appending(expectedNormalized).appending(stubFileName)\n        #expect(\n            FileManager.default.fileExists(atPath: expectedPath.string),\n            \"Expected file at normalized path: \\(expectedPath.string)\")\n    }\n\n    @Test(\n        \"Test paths with .. that normalize to valid paths\",\n        arguments: [\n            // Paths with .. that should fail\n            (\"safe/..\"),  // Normalizes to empty (current dir)\n            (\"a/../b\"),  // Normalizes to b\n            (\"a/b/../c\"),  // Normalizes to a/c\n        ]\n    )\n    func testPathsWithDotDotNormalization(path: String) async throws {\n        let rootPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: rootPath.string) }\n\n        let rootFd = try FileDescriptor.open(rootPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        #expect(throws: SecurePathError.invalidRelativePath.self) {\n            try rootFd.mkdirSecure(FilePath(path), makeIntermediates: true)\n        }\n    }\n\n    @Test(\n        \"Test paths with empty components (double slashes)\",\n        arguments: [\n            \"a//b\",  // Double slash in middle\n            \"a///b\",  // Triple slash\n            \"a//b//c\",  // Multiple double slashes\n        ]\n    )\n    func testPathsWithEmptyComponents(path: String) async throws {\n        let rootPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: rootPath.string) }\n\n        let rootFd = try FileDescriptor.open(rootPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        let stubFileName = \"stub.txt\"\n        let stubContent = Data(\"stub file content\".utf8)\n\n        // Should normalize and succeed (// becomes /)\n        try rootFd.mkdirSecure(FilePath(path), makeIntermediates: true) { dirFd in\n            let fd = openat(\n                dirFd.rawValue,\n                stubFileName,\n                O_WRONLY | O_CREAT | O_TRUNC,\n                0o644\n            )\n            guard fd >= 0 else {\n                throw Errno(rawValue: errno)\n            }\n            defer { close(fd) }\n\n            try stubContent.withUnsafeBytes { buffer in\n                guard let baseAddress = buffer.baseAddress else { return }\n                let written = write(fd, baseAddress, buffer.count)\n                guard written == buffer.count else {\n                    throw Errno(rawValue: errno)\n                }\n            }\n        }\n\n        // Verify the file exists somewhere under root (normalization should handle it)\n        // The exact location depends on how FilePath normalizes empty components\n        let normalizedPath = FilePath(path).lexicallyNormalized()\n        let expectedPath = rootPath.appending(normalizedPath.string).appending(stubFileName)\n        #expect(\n            FileManager.default.fileExists(atPath: expectedPath.string),\n            \"Expected file at normalized path: \\(expectedPath.string)\")\n    }\n\n    @Test(\"Test very deep nesting\")\n    func testDeepNesting() async throws {\n        let rootPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: rootPath.string) }\n\n        let rootFd = try FileDescriptor.open(rootPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        // Create a 100-level deep path\n        var deepPath = \"\"\n        for i in 0..<100 {\n            if i > 0 { deepPath += \"/\" }\n            deepPath += \"level\\(i)\"\n        }\n\n        let stubFileName = \"deep.txt\"\n        let stubContent = Data(\"deep file\".utf8)\n\n        try rootFd.mkdirSecure(FilePath(deepPath), makeIntermediates: true) { dirFd in\n            let fd = openat(\n                dirFd.rawValue,\n                stubFileName,\n                O_WRONLY | O_CREAT | O_TRUNC,\n                0o644\n            )\n            guard fd >= 0 else {\n                throw Errno(rawValue: errno)\n            }\n            defer { close(fd) }\n\n            try stubContent.withUnsafeBytes { buffer in\n                guard let baseAddress = buffer.baseAddress else { return }\n                let written = write(fd, baseAddress, buffer.count)\n                guard written == buffer.count else {\n                    throw Errno(rawValue: errno)\n                }\n            }\n        }\n\n        // Verify the deep file exists\n        let expectedPath = rootPath.appending(deepPath).appending(stubFileName)\n        #expect(FileManager.default.fileExists(atPath: expectedPath.string))\n    }\n\n    @Test(\"Test path with null byte\")\n    func testNullByteInPath() async throws {\n        let rootPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: rootPath.string) }\n\n        let rootFd = try FileDescriptor.open(rootPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        // Path with null byte - FilePath may handle this differently\n        // This tests that we don't crash or have unexpected behavior\n        let pathWithNull = \"file\\u{0000}.txt\"\n\n        // Try to create it - behavior depends on FilePath's null byte handling\n        // We mainly want to ensure it doesn't bypass security checks\n        do {\n            try rootFd.mkdirSecure(FilePath(pathWithNull), makeIntermediates: true) { _ in }\n\n            // If it succeeds, verify it stayed within root\n            let entries = try FileManager.default.contentsOfDirectory(atPath: rootPath.string)\n            for entry in entries {\n                let fullPath = rootPath.appending(entry)\n                let canonicalRoot = try rootFd.getCanonicalPath()\n                let canonicalEntry = try FileDescriptor.open(fullPath, .readOnly)\n                let canonicalEntryPath = try canonicalEntry.getCanonicalPath()\n                try? canonicalEntry.close()\n\n                // Verify entry is under root\n                #expect(\n                    canonicalEntryPath.string.hasPrefix(canonicalRoot.string + \"/\") || canonicalEntryPath.string == canonicalRoot.string,\n                    \"Entry escaped root: \\(canonicalEntryPath.string)\")\n            }\n        } catch {\n            // If it fails, that's also acceptable - just don't crash\n        }\n    }\n\n    @Test(\"Remove a regular file\")\n    func testRemoveRegularFile() throws {\n        let tempPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: tempPath.string) }\n\n        let rootFd = try FileDescriptor.open(tempPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        // Create a regular file\n        let filePath = tempPath.appending(\"testfile.txt\")\n        FileManager.default.createFile(atPath: filePath.string, contents: Data(\"test\".utf8))\n\n        // Verify file exists\n        #expect(FileManager.default.fileExists(atPath: filePath.string))\n\n        // Remove it\n        try rootFd.unlinkRecursiveSecure(filename: FilePath.Component(\"testfile.txt\"))\n\n        // Verify file is gone\n        #expect(!FileManager.default.fileExists(atPath: filePath.string))\n    }\n\n    @Test(\"Remove an empty directory\")\n    func testRemoveEmptyDirectory() throws {\n        let tempPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: tempPath.string) }\n\n        let rootFd = try FileDescriptor.open(tempPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        // Create an empty directory\n        let dirPath = tempPath.appending(\"emptydir\")\n        try FileManager.default.createDirectory(atPath: dirPath.string, withIntermediateDirectories: false)\n\n        // Verify directory exists\n        var isDir: ObjCBool = false\n        #expect(FileManager.default.fileExists(atPath: dirPath.string, isDirectory: &isDir))\n        #expect(isDir.boolValue)\n\n        // Remove it\n        try rootFd.unlinkRecursiveSecure(filename: FilePath.Component(\"emptydir\"))\n\n        // Verify directory is gone\n        #expect(!FileManager.default.fileExists(atPath: dirPath.string))\n    }\n\n    @Test(\"Remove a directory with nested files and subdirectories\")\n    func testRemoveNestedDirectory() throws {\n        let tempPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: tempPath.string) }\n\n        let rootFd = try FileDescriptor.open(tempPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        // Create nested structure:\n        // nested/\n        //   file1.txt\n        //   subdir/\n        //     file2.txt\n        //     deepdir/\n        //       file3.txt\n        let nestedPath = tempPath.appending(\"nested\")\n        let subdirPath = nestedPath.appending(\"subdir\")\n        let deepdirPath = subdirPath.appending(\"deepdir\")\n\n        try FileManager.default.createDirectory(atPath: deepdirPath.string, withIntermediateDirectories: true)\n        FileManager.default.createFile(atPath: nestedPath.appending(\"file1.txt\").string, contents: Data(\"1\".utf8))\n        FileManager.default.createFile(atPath: subdirPath.appending(\"file2.txt\").string, contents: Data(\"2\".utf8))\n        FileManager.default.createFile(atPath: deepdirPath.appending(\"file3.txt\").string, contents: Data(\"3\".utf8))\n\n        // Verify structure exists\n        #expect(FileManager.default.fileExists(atPath: nestedPath.string))\n        #expect(FileManager.default.fileExists(atPath: subdirPath.string))\n        #expect(FileManager.default.fileExists(atPath: deepdirPath.string))\n\n        // Remove entire tree\n        try rootFd.unlinkRecursiveSecure(filename: FilePath.Component(\"nested\"))\n\n        // Verify everything is gone\n        #expect(!FileManager.default.fileExists(atPath: nestedPath.string))\n    }\n\n    @Test(\"Remove non-existent file returns without error\")\n    func testRemoveNonExistent() throws {\n        let tempPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: tempPath.string) }\n\n        let rootFd = try FileDescriptor.open(tempPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        // Remove non-existent file should not throw\n        try rootFd.unlinkRecursiveSecure(filename: FilePath.Component(\"nonexistent.txt\"))\n    }\n\n    @Test(\"Remove symlink without following it\")\n    func testRemoveSymlink() throws {\n        let tempPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: tempPath.string) }\n\n        let rootFd = try FileDescriptor.open(tempPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        // Create target file and symlink\n        let targetPath = tempPath.appending(\"target.txt\")\n        let linkPath = tempPath.appending(\"link\")\n        FileManager.default.createFile(atPath: targetPath.string, contents: Data(\"target\".utf8))\n        try FileManager.default.createSymbolicLink(atPath: linkPath.string, withDestinationPath: \"target.txt\")\n\n        // Verify both exist\n        #expect(FileManager.default.fileExists(atPath: targetPath.string))\n        #expect(FileManager.default.fileExists(atPath: linkPath.string))\n\n        // Remove symlink\n        try rootFd.unlinkRecursiveSecure(filename: FilePath.Component(\"link\"))\n\n        // Verify symlink is gone but target remains\n        #expect(!FileManager.default.fileExists(atPath: linkPath.string))\n        #expect(FileManager.default.fileExists(atPath: targetPath.string))\n    }\n\n    @Test(\"Remove directory with mixed content (files, dirs, symlinks)\")\n    func testRemoveMixedDirectory() throws {\n        let tempPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: tempPath.string) }\n\n        let rootFd = try FileDescriptor.open(tempPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        // Create mixed structure:\n        // mixed/\n        //   file.txt\n        //   subdir/\n        //   link -> file.txt\n        let mixedPath = tempPath.appending(\"mixed\")\n        let subdirPath = mixedPath.appending(\"subdir\")\n\n        try FileManager.default.createDirectory(atPath: subdirPath.string, withIntermediateDirectories: true)\n        FileManager.default.createFile(atPath: mixedPath.appending(\"file.txt\").string, contents: Data(\"test\".utf8))\n        try FileManager.default.createSymbolicLink(\n            atPath: mixedPath.appending(\"link\").string,\n            withDestinationPath: \"file.txt\"\n        )\n\n        // Verify structure exists\n        #expect(FileManager.default.fileExists(atPath: mixedPath.string))\n\n        // Remove entire tree\n        try rootFd.unlinkRecursiveSecure(filename: FilePath.Component(\"mixed\"))\n\n        // Verify everything is gone\n        #expect(!FileManager.default.fileExists(atPath: mixedPath.string))\n    }\n\n    @Test(\"Guards against removing '.' component\")\n    func testGuardDotComponent() throws {\n        let tempPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: tempPath.string) }\n\n        let rootFd = try FileDescriptor.open(tempPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        // Should return without error and without removing anything\n        try rootFd.unlinkRecursiveSecure(filename: FilePath.Component(\".\"))\n\n        // Verify directory still exists\n        #expect(FileManager.default.fileExists(atPath: tempPath.string))\n    }\n\n    @Test(\"Guards against removing '..' component\")\n    func testGuardDotDotComponent() throws {\n        let tempPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: tempPath.string) }\n\n        let rootFd = try FileDescriptor.open(tempPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        // Should return without error and without removing anything\n        try rootFd.unlinkRecursiveSecure(filename: FilePath.Component(\"..\"))\n\n        // Verify directory still exists\n        #expect(FileManager.default.fileExists(atPath: tempPath.string))\n    }\n\n    @Test(\"Test mkdirSecure with empty path calls completion with parent\")\n    func testMkdirSecureEmptyPath() throws {\n        let rootPath = try createTempDirectory()\n        defer { try? FileManager.default.removeItem(atPath: rootPath.string) }\n\n        let rootFd = try FileDescriptor.open(rootPath, .readOnly, options: [.directory])\n        defer { try? rootFd.close() }\n\n        let stubFileName = \"root-level-file.txt\"\n        let stubContent = Data(\"root level content\".utf8)\n        var completionCalled = false\n\n        // Call mkdirSecure with empty path\n        try rootFd.mkdirSecure(FilePath(\"\"), makeIntermediates: false) { dirFd in\n            completionCalled = true\n\n            // Verify dirFd is the same as rootFd\n            #expect(dirFd.rawValue == rootFd.rawValue, \"Completion should receive the parent directory FD\")\n\n            // Create a file in the directory to verify we got the right FD\n            let fd = openat(\n                dirFd.rawValue,\n                stubFileName,\n                O_WRONLY | O_CREAT | O_TRUNC,\n                0o644\n            )\n            guard fd >= 0 else {\n                throw Errno(rawValue: errno)\n            }\n            defer { close(fd) }\n\n            try stubContent.withUnsafeBytes { buffer in\n                guard let baseAddress = buffer.baseAddress else { return }\n                let written = write(fd, baseAddress, buffer.count)\n                guard written == buffer.count else {\n                    throw Errno(rawValue: errno)\n                }\n            }\n        }\n\n        // Verify completion was called\n        #expect(completionCalled, \"Completion handler should be called for empty path\")\n\n        // Verify file was created at root level\n        let expectedPath = rootPath.appending(stubFileName)\n        #expect(FileManager.default.fileExists(atPath: expectedPath.string))\n\n        // Verify content\n        let readContent = try Data(contentsOf: URL(fileURLWithPath: expectedPath.string))\n        #expect(readContent == stubContent)\n    }\n\n    private func createTempDirectory() throws -> FilePath {\n        let tempURL = FileManager.default.temporaryDirectory\n            .appendingPathComponent(UUID().uuidString)\n        try FileManager.default.createDirectory(at: tempURL, withIntermediateDirectories: true)\n        return FilePath(tempURL.path)\n\n    }\n\n    private func createEntries(rootPath: FilePath, entries: [Entry], permissions: FilePermissions? = nil) throws {\n        for entry in entries {\n            switch entry {\n            case .regular(let path):\n                let fullPath = rootPath.appending(path)\n                // Create parent directories if needed\n                let parentPath = FilePath(fullPath.string).removingLastComponent()\n                if !FileManager.default.fileExists(atPath: parentPath.string) {\n                    try FileManager.default.createDirectory(\n                        atPath: parentPath.string,\n                        withIntermediateDirectories: true,\n                        attributes: permissions.map { [.posixPermissions: $0.rawValue] }\n                    )\n                }\n                FileManager.default.createFile(\n                    atPath: fullPath.string,\n                    contents: Data(\"test\".utf8)\n                )\n            case .directory(let path):\n                let fullPath = rootPath.appending(path)\n                try FileManager.default.createDirectory(\n                    atPath: fullPath.string,\n                    withIntermediateDirectories: true,\n                    attributes: permissions.map { [.posixPermissions: $0.rawValue] }\n                )\n            case .symlink(let target, let source):\n                let sourcePath = rootPath.appending(source)\n                // Create parent directories for source if needed\n                let parentPath = FilePath(sourcePath.string).removingLastComponent()\n                if !FileManager.default.fileExists(atPath: parentPath.string) {\n                    try FileManager.default.createDirectory(\n                        atPath: parentPath.string,\n                        withIntermediateDirectories: true,\n                        attributes: permissions.map { [.posixPermissions: $0.rawValue] }\n                    )\n                }\n                try FileManager.default.createSymbolicLink(\n                    atPath: sourcePath.string,\n                    withDestinationPath: target\n                )\n            }\n        }\n    }\n}\n\nenum Entry {\n    case regular(path: String)\n    case directory(path: String)\n    case symlink(target: String, source: String)\n}\n"
  },
  {
    "path": "Tests/ContainerizationOSTests/KeychainQueryTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import ContainerizationOS\n\nstruct KeychainQueryTests {\n    let securityDomain = \"com.example.container-testing-keychain\"\n    let hostname = \"testing-keychain.example.com\"\n    let username = \"containerization-test\"\n\n    let kq = KeychainQuery()\n\n    @Test(.enabled(if: !isCI))\n    func keychainQuery() throws {\n        defer { try? kq.delete(securityDomain: securityDomain, hostname: hostname) }\n\n        do {\n            try kq.save(securityDomain: securityDomain, hostname: hostname, username: username, password: \"foobar\")\n            #expect(try kq.exists(securityDomain: securityDomain, hostname: hostname))\n\n            let fetched = try kq.get(securityDomain: securityDomain, hostname: hostname)\n            let result = try #require(fetched)\n            #expect(result.username == username)\n            #expect(result.password == \"foobar\")\n        } catch KeychainQuery.Error.unhandledError(status: -25308) {\n            // ignore errSecInteractionNotAllowed\n        }\n    }\n\n    @Test(.enabled(if: !isCI))\n    func list() throws {\n        let hostname1 = \"testing-1-keychain.example.com\"\n        let hostname2 = \"testing-2-keychain.example.com\"\n\n        defer {\n            try? kq.delete(securityDomain: securityDomain, hostname: hostname1)\n            try? kq.delete(securityDomain: securityDomain, hostname: hostname2)\n        }\n\n        do {\n            try kq.save(securityDomain: securityDomain, hostname: hostname1, username: username, password: \"foobar\")\n            try kq.save(securityDomain: securityDomain, hostname: hostname2, username: username, password: \"foobar\")\n\n            let entries = try kq.list(securityDomain: securityDomain)\n\n            // Verify that both hostnames exist\n            let hostnames = entries.map { $0.hostname }\n            #expect(hostnames.contains(hostname1))\n            #expect(hostnames.contains(hostname2))\n\n            // Verify that the accounts exist\n            for entry in entries {\n                #expect(entry.username == username)\n            }\n        } catch KeychainQuery.Error.unhandledError(status: -25308) {\n            // ignore errSecInteractionNotAllowed\n        }\n    }\n\n    private static var isCI: Bool {\n        ProcessInfo.processInfo.environment[\"CI\"] != nil\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationOSTests/SocketTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport CShim\nimport Foundation\nimport Testing\n\n@testable import ContainerizationOS\n\n#if canImport(Darwin)\nimport Darwin\n#elseif canImport(Glibc)\nimport Glibc\n#elseif canImport(Musl)\nimport Musl\n#endif\n\n@Suite(\"Socket SCM_RIGHTS tests\")\nfinal class SocketTests {\n\n    /// Helper function to send a file descriptor via SCM_RIGHTS\n    private func sendFileDescriptor(socket: Socket, fd: Int32) throws {\n        var msg = msghdr()\n        var iov = iovec()\n        var buf: UInt8 = 0\n\n        iov.iov_base = withUnsafeMutablePointer(to: &buf) { UnsafeMutableRawPointer($0) }\n        iov.iov_len = 1\n\n        msg.msg_iov = withUnsafeMutablePointer(to: &iov) { $0 }\n        msg.msg_iovlen = 1\n\n        // Control message buffer for file descriptor\n        var cmsgBuf = [UInt8](repeating: 0, count: Int(CZ_CMSG_SPACE(Int(MemoryLayout<Int32>.size))))\n\n        msg.msg_control = withUnsafeMutablePointer(to: &cmsgBuf[0]) { UnsafeMutableRawPointer($0) }\n        msg.msg_controllen = socklen_t(cmsgBuf.count)\n\n        // Set up control message\n        let cmsgPtr = withUnsafeMutablePointer(to: &msg) { CZ_CMSG_FIRSTHDR($0) }\n        guard let cmsg = cmsgPtr else {\n            throw SocketError.invalidFileDescriptor\n        }\n\n        cmsg.pointee.cmsg_level = SOL_SOCKET\n        cmsg.pointee.cmsg_type = SCM_RIGHTS\n        cmsg.pointee.cmsg_len = socklen_t(CZ_CMSG_LEN(Int(MemoryLayout<Int32>.size)))\n\n        guard let dataPtr = CZ_CMSG_DATA(cmsg) else {\n            throw SocketError.invalidFileDescriptor\n        }\n\n        dataPtr.assumingMemoryBound(to: Int32.self).pointee = fd\n\n        let sendResult = withUnsafeMutablePointer(to: &msg) { msgPtr in\n            sendmsg(socket.fileDescriptor, msgPtr, 0)\n        }\n\n        guard sendResult >= 0 else {\n            throw SocketError.withErrno(\"sendmsg failed\", errno: errno)\n        }\n    }\n\n    @Test\n    func testSCMRightsFileDescriptorPassing() throws {\n        // Create a socketpair for testing\n        var fds: [Int32] = [0, 0]\n        let result = socketpair(AF_UNIX, SOCK_STREAM, 0, &fds)\n        try #require(result == 0, \"socketpair should succeed\")\n\n        defer {\n            close(fds[0])\n            close(fds[1])\n        }\n\n        // Use a dummy UnixType since we won't be using it for bind/connect/listen\n        let socketType = try UnixType(path: \"/tmp/dummy\")\n        let sendSocket = Socket(fd: fds[0], type: socketType, closeOnDeinit: false, connected: true)\n        let recvSocket = Socket(fd: fds[1], type: socketType, closeOnDeinit: false, connected: true)\n\n        // Create a temporary file to send its descriptor\n        let fileManager = FileManager.default\n        let tempDir = fileManager.uniqueTemporaryDirectory()\n        defer { try? fileManager.removeItem(at: tempDir) }\n\n        let testFilePath = tempDir.appending(path: \"test.txt\")\n        let testContent = \"Hello, SCM_RIGHTS!\"\n        try testContent.write(to: testFilePath, atomically: true, encoding: .utf8)\n\n        let testFileHandle = try FileHandle(forReadingFrom: testFilePath)\n        defer { try? testFileHandle.close() }\n\n        let originalFD = testFileHandle.fileDescriptor\n\n        try sendFileDescriptor(socket: sendSocket, fd: originalFD)\n        let receivedFd = try recvSocket.receiveFileDescriptor()\n        let receivedFileHandle = FileHandle(fileDescriptor: receivedFd)\n        defer { try? receivedFileHandle.close() }\n\n        try #require(receivedFileHandle.fileDescriptor != originalFD, \"Received FD should be different\")\n        try #require(receivedFileHandle.fileDescriptor >= 0, \"Received FD should be valid\")\n\n        let data = try receivedFileHandle.readToEnd()\n        try #require(data != nil, \"Should be able to read from received FD\")\n\n        let receivedContent = String(data: data!, encoding: .utf8)\n        #expect(receivedContent == testContent, \"Content should match original file\")\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationOSTests/UserTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationExtras\nimport Foundation\nimport Testing\n\n@testable import ContainerizationOS\n\n@Suite(\"User/Group parse tests\")\nfinal class UsersTests {\n    struct TestCase: Sendable {\n        let userString: String\n        let expect: User.ExecUser\n        let shouldThrow: Bool\n\n        init(_ userString: String, _ expect: User.ExecUser, _ shouldThrow: Bool) {\n            self.userString = userString\n            self.expect = expect\n            self.shouldThrow = shouldThrow\n        }\n    }\n\n    static func createFile(path: URL, content: Data) throws {\n        let parent = path.deletingLastPathComponent()\n        let fileManager = FileManager.default\n        try fileManager.createDirectory(at: parent, withIntermediateDirectories: true)\n        try content.write(to: path)\n    }\n\n    @Test\n    func testExecUserOnlyPasswd() throws {\n        let passwordContent = \"\"\"\n            root:x:0:0:root:/root:/bin/bash\n            daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\n            bin:x:2:2:bin:/bin:/usr/sbin/nologin\n            sys:x:3:3:sys:/dev:/usr/sbin/nologin\n            nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin\n            platform:x:1000:1000:Platform:/home/platform:/bin/sh\n            \"\"\"\n\n        let fileManager = FileManager.default\n        let tempDir = fileManager.uniqueTemporaryDirectory()\n        defer { try? fileManager.removeItem(at: tempDir) }\n        let passwdPath = tempDir.appending(path: \"etc/passwd\")\n        try Self.createFile(path: passwdPath, content: passwordContent.data(using: .ascii)!)\n\n        let testCases: [TestCase] = [\n            .init(\"root\", .init(uid: 0, gid: 0, sgids: [], home: \"/root\", shell: \"/bin/bash\"), false),\n            .init(\"0:0\", .init(uid: 0, gid: 0, sgids: [], home: \"/root\", shell: \"/bin/bash\"), false),\n            .init(\"platform\", .init(uid: 1000, gid: 1000, sgids: [], home: \"/home/platform\", shell: \"/bin/sh\"), false),\n            .init(\"65534\", .init(uid: 65534, gid: 65534, sgids: [], home: \"/nonexistent\", shell: \"/usr/sbin/nologin\"), false),\n            .init(\"should_fail\", .init(uid: 456, gid: 123, sgids: [], home: \"/undefined\", shell: \"\"), true),\n            .init(\":nouser\", .init(uid: 456, gid: 123, sgids: [], home: \"/undefined\", shell: \"\"), true),\n        ]\n\n        let groupPath = tempDir.appending(path: \"etc/group\")\n        for testCase in testCases {\n            if testCase.shouldThrow {\n                #expect(throws: User.Error.self) {\n                    try User.getExecUser(userString: testCase.userString, passwdPath: passwdPath, groupPath: groupPath)\n                }\n                continue\n            }\n            let user = try User.getExecUser(userString: testCase.userString, passwdPath: passwdPath, groupPath: groupPath)\n            #expect(testCase.expect.uid == user.uid)\n            #expect(testCase.expect.gid == user.gid)\n            #expect(testCase.expect.home == user.home)\n            #expect(testCase.expect.sgids == user.sgids)\n            #expect(testCase.expect.shell == user.shell)\n        }\n    }\n\n    @Test\n    func testExecUserNoPasswdFile() throws {\n        #expect(throws: User.Error.self) {\n            try User.getExecUser(\n                userString: \"root:root\",\n                passwdPath: URL(filePath: \"/foobar-passwd\"),\n                groupPath: URL(filePath: \"/foobar-group\")\n            )\n        }\n    }\n\n    @Test\n    func testExecUserPasswdGroup() throws {\n        let passwordContent = \"\"\"\n            root:x:0:0:root:/root:/bin/bash\n            daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\n            bin:x:2:2:bin:/bin:/usr/sbin/nologin\n            sys:x:3:3:sys:/dev:/usr/sbin/nologin\n            backup:x:34:34:backup:/var/backups:/usr/sbin/nologin\n            nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin\n            platform:x:1000:1000:platform:/home/platform:/bin/bash\n            \"\"\"\n\n        let groupContent = \"\"\"\n            root:x:0:\n            daemon:x:1:\n            bin:x:2:\n            adm:x:4:platform\n            tape:x:26:\n            sudo:x:27:platform\n            audio:x:29:platform\n            video:x:44:platform\n            nogroup:x:65534:\n            platform:x:1000:\n            \"\"\"\n\n        let fileManager = FileManager.default\n        let tempDir = fileManager.uniqueTemporaryDirectory()\n        defer { try? fileManager.removeItem(at: tempDir) }\n        let passwdPath = tempDir.appending(path: \"etc/passwd\")\n        let groupPath = tempDir.appending(path: \"etc/group\")\n        try Self.createFile(path: passwdPath, content: passwordContent.data(using: .ascii)!)\n        try Self.createFile(path: groupPath, content: groupContent.data(using: .ascii)!)\n\n        let testCases: [TestCase] = [\n            .init(\"root:bin\", .init(uid: 0, gid: 2, sgids: [2], home: \"/root\", shell: \"/bin/bash\"), false),\n            .init(\"daemon:platform\", .init(uid: 1, gid: 1000, sgids: [1000], home: \"/usr/sbin\", shell: \"/usr/sbin/nologin\"), false),\n            .init(\"platform\", .init(uid: 1000, gid: 1000, sgids: [4, 27, 29, 44], home: \"/home/platform\", shell: \"/bin/bash\"), false),\n            .init(\"nobody\", .init(uid: 65534, gid: 65534, sgids: [], home: \"/nonexistent\", shell: \"/usr/sbin/nologin\"), false),\n            .init(\"2:1000\", .init(uid: 2, gid: 1000, sgids: [1000], home: \"/bin\", shell: \"/usr/sbin/nologin\"), false),\n        ]\n\n        for testCase in testCases {\n            if testCase.shouldThrow {\n                #expect(throws: User.Error.self) {\n                    try User.getExecUser(userString: testCase.userString, passwdPath: passwdPath, groupPath: groupPath)\n                }\n            }\n            let user = try User.getExecUser(userString: testCase.userString, passwdPath: passwdPath, groupPath: groupPath)\n            #expect(testCase.expect.uid == user.uid)\n            #expect(testCase.expect.gid == user.gid)\n            #expect(testCase.expect.home == user.home)\n            #expect(Set(testCase.expect.sgids) == Set(user.sgids))\n            #expect(testCase.expect.shell == user.shell)\n        }\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationTests/ContainerManagerTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Containerization\nimport ContainerizationArchive\nimport ContainerizationError\nimport ContainerizationExtras\nimport Foundation\nimport Testing\n\n@testable import Containerization\n\nprivate struct NilGatewayInterface: Interface {\n    let ipv4Address: CIDRv4\n    let ipv4Gateway: IPv4Address? = nil\n    let macAddress: MACAddress? = nil\n\n    init() {\n        self.ipv4Address = try! CIDRv4(\"192.168.64.2/24\")\n    }\n}\n\nprivate struct NilGatewayNetwork: Network {\n    mutating func createInterface(_ id: String) throws -> Interface? {\n        NilGatewayInterface()\n    }\n\n    mutating func releaseInterface(_ id: String) throws {}\n}\n\n@Suite\nstruct ContainerManagerTests {\n    @Test func testCreateThrowsWhenGatewayMissing() async throws {\n        let fm = FileManager.default\n        let root = fm.uniqueTemporaryDirectory(create: true)\n        defer { try? fm.removeItem(at: root) }\n\n        let kernelPath = root.appendingPathComponent(\"vmlinux\")\n        fm.createFile(atPath: kernelPath.path, contents: Data(), attributes: nil)\n        let initfsPath = root.appendingPathComponent(\"initfs.ext4\")\n        fm.createFile(atPath: initfsPath.path, contents: Data(), attributes: nil)\n\n        let kernel = Kernel(path: kernelPath, platform: .linuxArm)\n        let initfs = Mount.block(format: \"ext4\", source: initfsPath.path, destination: \"/\")\n\n        var manager = try ContainerManager(\n            kernel: kernel,\n            initfs: initfs,\n            root: root,\n            network: NilGatewayNetwork()\n        )\n\n        let tempDir = fm.uniqueTemporaryDirectory()\n        defer { try? fm.removeItem(at: tempDir) }\n\n        let tarPath = Foundation.Bundle.module.url(forResource: \"scratch\", withExtension: \"tar\")!\n        let reader = try ArchiveReader(format: .pax, filter: .none, file: tarPath)\n        let rejectedPaths = try reader.extractContents(to: tempDir)\n        #expect(rejectedPaths.isEmpty)\n\n        let images = try await manager.imageStore.load(from: tempDir)\n        let image = images.first!\n\n        let rootfsPath = root.appendingPathComponent(\"rootfs.ext4\")\n        fm.createFile(atPath: rootfsPath.path, contents: Data(), attributes: nil)\n        let rootfs = Mount.block(format: \"ext4\", source: rootfsPath.path, destination: \"/\")\n\n        do {\n            _ = try await manager.create(\"test-nil-gateway\", image: image, rootfs: rootfs) { _ in }\n            #expect(Bool(false), \"expected invalidState error for missing ipv4 gateway\")\n        } catch let error as ContainerizationError {\n            #expect(error.code == .invalidState)\n            #expect(error.message.contains(\"missing ipv4 gateway\"))\n        } catch {\n            #expect(Bool(false), \"unexpected error: \\(error)\")\n        }\n    }\n\n    @Test func testNetworkingFalseSkipsInterfaceCreation() async throws {\n        let fm = FileManager.default\n        let root = fm.uniqueTemporaryDirectory(create: true)\n        defer { try? fm.removeItem(at: root) }\n\n        let kernelPath = root.appendingPathComponent(\"vmlinux\")\n        fm.createFile(atPath: kernelPath.path, contents: Data(), attributes: nil)\n        let initfsPath = root.appendingPathComponent(\"initfs.ext4\")\n        fm.createFile(atPath: initfsPath.path, contents: Data(), attributes: nil)\n\n        let kernel = Kernel(path: kernelPath, platform: .linuxArm)\n        let initfs = Mount.block(format: \"ext4\", source: initfsPath.path, destination: \"/\")\n\n        // Use NilGatewayNetwork — with networking: true this would throw invalidState,\n        // but with networking: false the network's createInterface() is never called.\n        var manager = try ContainerManager(\n            kernel: kernel,\n            initfs: initfs,\n            root: root,\n            network: NilGatewayNetwork()\n        )\n\n        let tempDir = fm.uniqueTemporaryDirectory()\n        defer { try? fm.removeItem(at: tempDir) }\n\n        let tarPath = Foundation.Bundle.module.url(forResource: \"scratch\", withExtension: \"tar\")!\n        let reader = try ArchiveReader(format: .pax, filter: .none, file: tarPath)\n        let rejectedPaths = try reader.extractContents(to: tempDir)\n        #expect(rejectedPaths.isEmpty)\n\n        let images = try await manager.imageStore.load(from: tempDir)\n        let image = images.first!\n\n        let rootfsPath = root.appendingPathComponent(\"rootfs.ext4\")\n        fm.createFile(atPath: rootfsPath.path, contents: Data(), attributes: nil)\n        let rootfs = Mount.block(format: \"ext4\", source: rootfsPath.path, destination: \"/\")\n\n        // With networking: false, NilGatewayNetwork.createInterface() is never called,\n        // so we should not get the \"missing ipv4 gateway\" error.\n        // The container creation will fail for other reasons (dummy VMM), but the\n        // configuration closure should see empty interfaces.\n        var closureWasCalled = false\n        do {\n            _ = try await manager.create(\n                \"test-no-networking\",\n                image: image,\n                rootfs: rootfs,\n                networking: false\n            ) { config in\n                closureWasCalled = true\n                #expect(config.interfaces.isEmpty)\n                #expect(config.dns == nil)\n            }\n        } catch {\n            // Container creation may fail due to dummy kernel/VMM — that's expected.\n            // The key assertion is in the configuration closure above.\n            let description = String(describing: error)\n            #expect(!description.contains(\"missing ipv4 gateway\"))\n        }\n        #expect(closureWasCalled, \"configuration closure must be invoked to validate interfaces\")\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationTests/DNSTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import Containerization\n\nstruct DNSTests {\n\n    @Test func dnsResolvConfWithAllFields() {\n        let dns = DNS(\n            nameservers: [\"8.8.8.8\", \"1.1.1.1\"],\n            domain: \"example.com\",\n            searchDomains: [\"internal.com\", \"test.com\"],\n            options: [\"ndots:2\", \"timeout:1\"]\n        )\n\n        let expected = \"nameserver 8.8.8.8\\nnameserver 1.1.1.1\\ndomain example.com\\nsearch internal.com test.com\\noptions ndots:2 timeout:1\\n\"\n        #expect(dns.resolvConf == expected)\n    }\n\n    @Test func dnsResolvConfWithEmptyFields() {\n        let dns = DNS(\n            nameservers: [],\n            domain: nil,\n            searchDomains: [],\n            options: []\n        )\n\n        // Should return empty string when all fields are empty\n        #expect(dns.resolvConf == \"\")\n    }\n\n    @Test func dnsResolvConfWithOnlyNameservers() {\n        let dns = DNS(nameservers: [\"8.8.8.8\"])\n\n        let expected = \"nameserver 8.8.8.8\\n\"\n        #expect(dns.resolvConf == expected)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationTests/HashTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import Containerization\n\nstruct HashTests {\n\n    @Test func hashMountSourceWithValidString() throws {\n        let result = try hashMountSource(source: \"/valid/path\")\n\n        // Should produce a non-empty hash\n        #expect(!result.isEmpty)\n\n        // Same input should produce same hash (deterministic)\n        let result2 = try hashMountSource(source: \"/valid/path\")\n        #expect(result == result2)\n\n        // Different inputs should produce different hashes\n        let result3 = try hashMountSource(source: \"/different/path\")\n        #expect(result != result3)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationTests/HostsTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import Containerization\n\nstruct HostsTests {\n\n    @Test func hostsEntryRenderedWithAllFields() {\n        let entry = Hosts.Entry(\n            ipAddress: \"192.168.1.100\",\n            hostnames: [\"myserver\", \"server.local\"],\n            comment: \"My local server\"\n        )\n\n        let expected = \"192.168.1.100 myserver server.local # My local server \"\n        #expect(entry.rendered == expected)\n    }\n\n    @Test func hostsEntryRenderedWithoutComment() {\n        let entry = Hosts.Entry(\n            ipAddress: \"10.0.0.1\",\n            hostnames: [\"gateway\"]\n        )\n\n        let expected = \"10.0.0.1 gateway\"\n        #expect(entry.rendered == expected)\n    }\n\n    @Test func hostsEntryRenderedWithEmptyHostnames() {\n        let entry = Hosts.Entry(\n            ipAddress: \"172.16.0.1\",\n            hostnames: [],\n            comment: \"Empty hostnames\"\n        )\n\n        let expected = \"172.16.0.1 # Empty hostnames \"\n        #expect(entry.rendered == expected)\n    }\n\n    @Test func hostsFileWithCommentAndEntries() {\n        let hosts = Hosts(\n            entries: [\n                Hosts.Entry(ipAddress: \"127.0.0.1\", hostnames: [\"localhost\"]),\n                Hosts.Entry(ipAddress: \"192.168.1.10\", hostnames: [\"server\"], comment: \"Main server\"),\n            ],\n            comment: \"Generated hosts file\"\n        )\n\n        let expected = \"# Generated hosts file\\n127.0.0.1 localhost\\n192.168.1.10 server # Main server \\n\"\n        #expect(hosts.hostsFile == expected)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationTests/ImageTests/ContainsAuth.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOCI\nimport Foundation\n\ninternal protocol ContainsAuth {\n\n}\n\nextension ContainsAuth {\n    static var hasRegistryCredentials: Bool {\n        authentication != nil\n    }\n\n    static var authentication: Authentication? {\n        let env = ProcessInfo.processInfo.environment\n        guard let password = env[\"REGISTRY_TOKEN\"],\n            let username = env[\"REGISTRY_USERNAME\"]\n        else {\n            return nil\n        }\n        return BasicAuthentication(username: username, password: password)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationTests/ImageTests/ImageStoreImagePullTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//\n\nimport ContainerizationOCI\nimport Crypto\nimport Foundation\nimport NIO\nimport Testing\n\n@testable import Containerization\n\n@Suite\nfinal class ImageStoreImagePullTests {\n    let store: ImageStore\n    let dir: URL\n    let contentStore: ContentStore\n\n    public init() {\n        let dir = FileManager.default.uniqueTemporaryDirectory(create: true)\n        let cs = try! LocalContentStore(path: dir)\n        let store = try! ImageStore(path: dir, contentStore: cs)\n        self.dir = dir\n        self.store = store\n        self.contentStore = cs\n    }\n\n    deinit {\n        try! FileManager.default.removeItem(at: self.dir)\n    }\n\n    @Test func testPullImageWithoutIndex() async throws {\n        let img = try await self.store.pull(reference: \"ghcr.io/apple/containerization/dockermanifestimage:0.0.2\")\n\n        let rootDescriptor = img.descriptor\n        let index: ContainerizationOCI.Index = try await contentStore.get(digest: rootDescriptor.digest)!\n\n        #expect(index.manifests.count == 1)\n        let desc = index.manifests.first!\n        #expect(desc.platform!.architecture == \"amd64\")\n\n        await #expect(throws: Never.self) {\n            let manifest: ContainerizationOCI.Manifest = try await self.contentStore.get(digest: desc.digest)!\n            let _: ContainerizationOCI.Image = try await self.contentStore.get(digest: manifest.config.digest)!\n            for layer in manifest.layers {\n                _ = try await self.contentStore.get(digest: layer.digest)!\n            }\n        }\n    }\n\n    @Test(\n        arguments: [\n            (Platform(arch: \"arm64\", os: \"linux\", variant: \"v8\"), imagePullArm64Layers),\n            (Platform(arch: \"amd64\", os: \"linux\"), imagePullAmd64Layers),\n            (nil, imagePullTestAllLayers),\n        ])\n    func testPullSinglePlatform(platform: Platform?, expectLayers: [String]) async throws {\n        let img = try await self.store.pull(\n            reference: \"ghcr.io/linuxcontainers/alpine:3.20@sha256:0a6a86d44d7f93c4f2b8dea7f0eee64e72cb98635398779f3610949632508d57\", platform: platform)\n        let rootDescriptor = img.descriptor\n        let index: ContainerizationOCI.Index = try await contentStore.get(digest: rootDescriptor.digest)!\n        var foundMatch = false\n        for desc in index.manifests {\n            if let platform {\n                if desc.platform != platform {\n                    continue\n                }\n            }\n            foundMatch = true\n            await #expect(throws: Never.self) {\n                let manifest: ContainerizationOCI.Manifest = try await self.contentStore.get(digest: desc.digest)!\n                let _: ContainerizationOCI.Image = try await self.contentStore.get(digest: manifest.config.digest)!\n                for layer in manifest.layers {\n                    _ = try await self.contentStore.get(digest: layer.digest)!\n                }\n            }\n        }\n        #expect(foundMatch)\n        let contentPath = dir.appendingPathComponent(\"blobs/sha256\")\n        let filesOnDisk = try FileManager.default.contentsOfDirectory(at: contentPath, includingPropertiesForKeys: nil).map {\n            $0.lastPathComponent\n        }.sorted()\n        #expect(filesOnDisk == expectLayers)\n    }\n\n    @Test func testPullWithSha() async throws {\n        let sha = \"sha256:0a6a86d44d7f93c4f2b8dea7f0eee64e72cb98635398779f3610949632508d57\"\n        let r = \"ghcr.io/linuxcontainers/alpine:3.20@\\(sha)\"\n        let img = try await self.store.pull(reference: r, platform: .current)\n        #expect(img.descriptor.digest == sha)\n    }\n}\n\nlet imagePullTestAllLayers = [\n    \"013c522f9494ecda30dc8fbee7805b59c773573fd080c74e6835def22547bd07\",\n    \"037316e2a3a13e6d7e15057d3ede6ad063f15c92216778576ee88a74e6f7c6fc\",\n    \"0566dbe8e93e20dbfebc6b023399a6eb337719faf1d11dab57f975286c198a00\",\n    \"0928a8adc0d420ddda0d25c76e95282534a5b69b13ffcbb6ddbc41c50fc77550\",\n    \"0a6a86d44d7f93c4f2b8dea7f0eee64e72cb98635398779f3610949632508d57\",\n    \"0a9a5dfd008f05ebc27e4790db0709a29e527690c21bcbcd01481eaeb6bb49dc\",\n    \"0c11ea92e0d923e7812d258defbe6788642547fa347969a3dbd7bb7cbc0a9666\",\n    \"156724324a3250a38177b0328390d7efb4ba85d7e095ad7af9ca19a3cd46f855\",\n    \"1c2a87b1633d21ffcd8192bc84f9bed0c479bbcfbcd8b76b9ca1b8bf8bc61516\",\n    \"2803bd9fd5a5e53bc39c576b3e7eaf4839ec77dcc1274c6ad9f7d534ccc566c7\",\n    \"2d2e65d21b1f1a7cf14b99e54809bb4eee749fa9145d1e263279e18e246e5e1b\",\n    \"34bac5d0022b2997fdfd5c678521d6afe58a4ea6c65d5d31e3ece0be141158ea\",\n    \"3e6ec69548a14d7bf37b242f02f26dd41c69e9c510225078ca1f241ef249b3df\",\n    \"423949aec9a2fe60140a59926634f90979ac19878957becf9902dcc547592a44\",\n    \"4817c12fc96d333e818e9f56f22a7c8683bd3ca8b0c04ce45e188dc6aaf8e5c3\",\n    \"4e32c214e82a5d6ccb62b58fb42405fc961c69da5fe02c670f1e4c62c8eb6fbb\",\n    \"4ea6a163031004a9a61288b7a5ffbf73d84115d398abe5180caeb15442d1a5fe\",\n    \"4f0bb7ea5efffa5762fc231a403f232ca3ea43ef6db18d4bf52aeca8c15d7dec\",\n    \"55a8c211d2e969b7b7e9e4825853cf24a75cbbcaf7728db15840c1514838a23d\",\n    \"5c979effb79226e255a01eaeb2a525bd12019c02eba2b76f6e0726dd2701508e\",\n    \"63e2abc26a64dea41796995524777edc558e143bea4929f06954c52706363f33\",\n    \"6d8b5334139bdee0462dd4d6cc85fdec98ad4d97155075973432ec4ff67906c9\",\n    \"6f3c7dfa949497fb255d0a28c244e7add0d52ae6318b45947a8a2940d846b2e2\",\n    \"718fbe9a22ec3da853bdbd5d8112f2dc8ba41f30d46899b3792242f16a0f8b41\",\n    \"744d40c360fd0988b20b15ab845d3db943817b027f38ea6850361bab4ac916be\",\n    \"76a0ff976fd7cf0f21858535989ef59ac2ee64a3f1bf1b68d98d15138cd46afa\",\n    \"772078ddbdee5be52d429e08f953aaad6715a90d7e4d6496eb1cd4004efa8a95\",\n    \"7c6bf3be7c8016421fb3033e19b6a313f264093e1ac9e77c9f931ade0d61b3f7\",\n    \"7f608f0a59b5b3717cdd3cc61ef59c329d3c2c16c5fc6963b3b13360d43841c0\",\n    \"81fc5885a3ad37110bf576934de28326e1194bd943e020bc3924502335fdf181\",\n    \"84df3263e35ed35440625ae0ebb5b1c3d00f57ffcec61188015d5217988a8b35\",\n    \"85b46e4c8e4841ce7964ce897a07a4d9df7d589322593fb600dd428e47d635ae\",\n    \"872bd582507dfe35ccd496fcd128f61963620053a722b517353f3d9df46412e9\",\n    \"8a9fb51ac81600da44afb1c4a5df4745d23eca0cc5d924f989c074f3da7a9440\",\n    \"90bb43c8fe064682d965fe27b1ca0cf2b42cf0273914cc20a4e636e174ccdaf5\",\n    \"9368b67dac9dc00ca8dbbd25b6f148fd6229b01dac5ff3d89281bd296cf196c6\",\n    \"94e9d8af22013aabf0edcaf42950c88b0a1350c3a9ce076d61b98a535a673dd9\",\n    \"9cbaf16e9229ef1466c71cf97a75ddd7d2041522012dd1bcace0d56a9ad77688\",\n    \"9cfe406db828239417e29e2c00bdf196c32b39ffcead4c2e28cdf60ff8a8dd58\",\n    \"adc8e49a814d3e4f73ccdd1d26d4cbd3f1a338b4e136a55c092bdcca57863225\",\n    \"b1ca1bb0a5f203b48e1ca60861ae852f49b910ce8488c19c392a3bc7ee31b072\",\n    \"b3d7db73e90671cb6b7925cc878d43a2781451bed256cf0626110f5386cdd4dc\",\n    \"bfc9829f240e42bab6b756c64179b8e73317baf0e9a8940ea1571cb2f29efcc3\",\n    \"c70d93f05189a8a6a10ba5657b8e89e849f2c7491d76587178f75d9fca228bf1\",\n    \"c9813c0f5a2f289ea6175876fd973d6d8adcd495da4a23e9273600c8f0a761c5\",\n    \"c9aedc9d4e47fa9429e5c329420d8a93e16c433e361d0f9281565ed4da3c057e\",\n    \"d27e7628ef6e28a3e91cdc1ef1f998a703d356067ecedddfe9e9281e36d8c9f9\",\n    \"ef99f4640fe11015a03439935b827bff242d0db64db27db005a31ab4497db4a2\",\n    \"f2c7f3c3fecbf01204eccd798e2f77b0003a8567927a5d6242fd3ed81727fee9\",\n    \"f882dda529d0cd4b586a10a7f60048c7f8faaff26d6672008d0478b8b004bc63\",\n]\n\nlet imagePullArm64Layers = [\n    \"0a6a86d44d7f93c4f2b8dea7f0eee64e72cb98635398779f3610949632508d57\",\n    \"3e6ec69548a14d7bf37b242f02f26dd41c69e9c510225078ca1f241ef249b3df\",\n    \"423949aec9a2fe60140a59926634f90979ac19878957becf9902dcc547592a44\",\n    \"76a0ff976fd7cf0f21858535989ef59ac2ee64a3f1bf1b68d98d15138cd46afa\",\n    \"94e9d8af22013aabf0edcaf42950c88b0a1350c3a9ce076d61b98a535a673dd9\",\n]\n\nlet imagePullAmd64Layers = [\n    \"0a6a86d44d7f93c4f2b8dea7f0eee64e72cb98635398779f3610949632508d57\",\n    \"0a9a5dfd008f05ebc27e4790db0709a29e527690c21bcbcd01481eaeb6bb49dc\",\n    \"156724324a3250a38177b0328390d7efb4ba85d7e095ad7af9ca19a3cd46f855\",\n    \"1c2a87b1633d21ffcd8192bc84f9bed0c479bbcfbcd8b76b9ca1b8bf8bc61516\",\n    \"4ea6a163031004a9a61288b7a5ffbf73d84115d398abe5180caeb15442d1a5fe\",\n]\n"
  },
  {
    "path": "Tests/ContainerizationTests/ImageTests/ImageStoreTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//\n\nimport ContainerizationArchive\nimport ContainerizationExtras\nimport ContainerizationOCI\nimport Foundation\nimport Testing\n\n@testable import Containerization\n\n@Suite\npublic class ImageStoreTests: ContainsAuth {\n    let store: ImageStore\n    let dir: URL\n\n    public init() {\n        let dir = FileManager.default.uniqueTemporaryDirectory(create: true)\n        let cs = try! LocalContentStore(path: dir)\n        let store = try! ImageStore(path: dir, contentStore: cs)\n        self.dir = dir\n        self.store = store\n    }\n\n    deinit {\n        try! FileManager.default.removeItem(at: self.dir)\n    }\n\n    @Test func testImageStoreOperation() async throws {\n        let fileManager = FileManager.default\n        let tempDir = fileManager.uniqueTemporaryDirectory()\n        defer {\n            try? fileManager.removeItem(at: tempDir)\n        }\n\n        let tarPath = Foundation.Bundle.module.url(forResource: \"scratch\", withExtension: \"tar\")!\n        let reader = try ArchiveReader(format: .pax, filter: .none, file: tarPath)\n        let rejectedPaths = try reader.extractContents(to: tempDir)\n        #expect(rejectedPaths.count == 0, \"unexpected rejected paths [\\(rejectedPaths)]\")\n\n        let _ = try await self.store.load(from: tempDir)\n        let loaded = try await self.store.load(from: tempDir)\n        let expectedLoadedImage = \"registry.local/integration-tests/scratch:latest\"\n        #expect(loaded.first!.reference == \"registry.local/integration-tests/scratch:latest\")\n\n        guard let authentication = Self.authentication else {\n            return\n        }\n        let imageReference = \"ghcr.io/apple/containerization/dockermanifestimage:0.0.2\"\n        let busyboxImage = try await self.store.pull(reference: imageReference, auth: authentication)\n\n        let got = try await self.store.get(reference: imageReference)\n        #expect(got.descriptor == busyboxImage.descriptor)\n\n        let newTag = \"registry.local/integration-tests/dockermanifestimage:latest\"\n        let _ = try await self.store.tag(existing: imageReference, new: newTag)\n\n        let tempFile = self.dir.appending(path: \"export.tar\")\n        try await self.store.save(references: [imageReference, expectedLoadedImage], out: tempFile)\n    }\n\n    @Test(.disabled(\"External users cannot push images, disable while we find a better solution\"))\n    func testImageStorePush() async throws {\n        guard let authentication = Self.authentication else {\n            return\n        }\n        let imageReference = \"ghcr.io/apple/containerization/dockermanifestimage:0.0.2\"\n\n        let remoteImageName = \"ghcr.io/apple/test-images/image-push\"\n        let epoch = Int(Date().timeIntervalSince1970.description)\n        let tag = epoch != nil ? String(epoch!) : \"latest\"\n        let upstreamTag = \"\\(remoteImageName):\\(tag)\"\n        let _ = try await self.store.tag(existing: imageReference, new: upstreamTag)\n        try await self.store.push(reference: upstreamTag, auth: authentication)\n    }\n\n    @Test func testLoadImageWithoutAnnotations() async throws {\n        let fileManager = FileManager.default\n        let tempDir = fileManager.uniqueTemporaryDirectory()\n        defer {\n            try? fileManager.removeItem(at: tempDir)\n        }\n\n        let tarPath = Foundation.Bundle.module.url(forResource: \"scratch_no_annotations\", withExtension: \"tar\")!\n        let reader = try ArchiveReader(format: .pax, filter: .none, file: tarPath)\n        let rejectedPaths = try reader.extractContents(to: tempDir)\n        #expect(rejectedPaths.count == 0, \"unexpected rejected paths [\\(rejectedPaths)]\")\n\n        let loaded = try await self.store.load(from: tempDir)\n\n        #expect(loaded.count == 1)\n\n        let reference = loaded.first!.reference\n        #expect(reference.hasPrefix(\"untagged@sha256:\"))\n\n        let retrieved = try await self.store.get(reference: reference)\n        #expect(retrieved.reference == reference)\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationTests/ImageTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOCI\nimport Foundation\nimport Testing\n\n@testable import Containerization\n\nstruct ImageTests {\n\n    @Test func imageDescriptionComputedProperties() {\n        let descriptor = Descriptor(\n            mediaType: \"application/vnd.oci.image.manifest.v1+json\",\n            digest: \"sha256:abc123def456\",\n            size: 1024\n        )\n        let description = Image.Description(reference: \"myapp:latest\", descriptor: descriptor)\n\n        #expect(description.digest == \"sha256:abc123def456\")\n        #expect(description.mediaType == \"application/vnd.oci.image.manifest.v1+json\")\n        #expect(description.reference == \"myapp:latest\")\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationTests/KernelTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n//\n\nimport Foundation\nimport Testing\n\n@testable import Containerization\n\nfinal class KernelTests {\n    @Test func kernelArgs() {\n        let commandLine = Kernel.CommandLine(debug: false, panic: 0)\n        let kernel = Kernel(path: .init(fileURLWithPath: \"\"), platform: .linuxArm, commandline: commandLine)\n\n        let expected = \"console=hvc0 tsc=reliable panic=0\"\n        let cmdline = kernel.commandLine.kernelArgs.joined(separator: \" \")\n        #expect(cmdline == expected)\n    }\n\n    @Test func kernelDebugArgs() {\n        let cmdLine = Kernel.CommandLine(debug: true, panic: 0)\n        let kernel = Kernel(path: .init(fileURLWithPath: \"\"), platform: .linuxArm, commandline: cmdLine)\n\n        let expected = \"console=hvc0 tsc=reliable debug panic=0\"\n        let cmdline = kernel.commandLine.kernelArgs.joined(separator: \" \")\n        #expect(cmdline == expected)\n    }\n\n    @Test func kernelCommandLineInitWithDebugTrue() {\n        let commandLine = Kernel.CommandLine(debug: true, panic: 5, initArgs: [\"--verbose\"])\n\n        #expect(commandLine.kernelArgs == [\"console=hvc0\", \"tsc=reliable\", \"debug\", \"panic=5\"])\n        #expect(commandLine.initArgs == [\"--verbose\"])\n    }\n\n    @Test func kernelCommandLineMutatingMethods() {\n        var commandLine = Kernel.CommandLine(kernelArgs: [\"console=hvc0\"], initArgs: [])\n\n        commandLine.addDebug()\n        commandLine.addPanic(level: 10)\n\n        #expect(commandLine.kernelArgs == [\"console=hvc0\", \"debug\", \"panic=10\"])\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationTests/LinuxContainerTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOCI\nimport Foundation\nimport Testing\n\n@testable import Containerization\n\nstruct LinuxContainerTests {\n\n    @Test func processInitFromImageConfigWithAllFields() {\n        let imageConfig = ImageConfig(\n            user: \"appuser\",\n            env: [\"NODE_ENV=production\", \"PORT=3000\"],\n            entrypoint: [\"/usr/bin/node\"],\n            cmd: [\"app.js\", \"--verbose\"],\n            workingDir: \"/app\"\n        )\n\n        let process = LinuxProcessConfiguration(from: imageConfig)\n\n        #expect(process.workingDirectory == \"/app\")\n        #expect(process.environmentVariables == [\"NODE_ENV=production\", \"PORT=3000\"])\n        #expect(process.arguments == [\"/usr/bin/node\", \"app.js\", \"--verbose\"])\n        #expect(process.user.username == \"appuser\")\n    }\n\n    @Test func processInitFromImageConfigWithNilValues() {\n        let imageConfig = ImageConfig(\n            user: nil,\n            env: nil,\n            entrypoint: nil,\n            cmd: nil,\n            workingDir: nil\n        )\n\n        let process = LinuxProcessConfiguration(from: imageConfig)\n\n        #expect(process.workingDirectory == \"/\")\n        #expect(process.environmentVariables == [])\n        #expect(process.arguments == [])\n        #expect(process.user.username == \"\")  // Default User() has empty string username\n    }\n\n    @Test func processInitFromImageConfigEntrypointAndCmdConcatenation() {\n        let imageConfig = ImageConfig(\n            entrypoint: [\"/bin/sh\", \"-c\"],\n            cmd: [\"echo 'hello'\", \"&&\", \"sleep 10\"]\n        )\n\n        let process = LinuxProcessConfiguration(from: imageConfig)\n\n        #expect(process.arguments == [\"/bin/sh\", \"-c\", \"echo 'hello'\", \"&&\", \"sleep 10\"])\n    }\n}\n"
  },
  {
    "path": "Tests/ContainerizationTests/MountTests.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\nimport Testing\n\n@testable import Containerization\n\nstruct MountTests {\n\n    @Test func mountShareCreatesVirtiofsMount() {\n        let mount = Mount.share(\n            source: \"/host/shared\",\n            destination: \"/guest/shared\",\n            options: [\"rw\", \"noatime\"],\n            runtimeOptions: [\"tag=shared\"]\n        )\n\n        #expect(mount.type == \"virtiofs\")\n        #expect(mount.source == \"/host/shared\")\n        #expect(mount.destination == \"/guest/shared\")\n        #expect(mount.options == [\"rw\", \"noatime\"])\n\n        if case .virtiofs(let opts) = mount.runtimeOptions {\n            #expect(opts == [\"tag=shared\"])\n        } else {\n            #expect(Bool(false), \"Expected virtiofs runtime options\")\n        }\n    }\n}\n"
  },
  {
    "path": "Tests/TestImages/dockermanifestimage/Dockerfile",
    "content": "# This image is built as a single platform image with media type application/vnd.docker.distribution.manifest.v2+json\nFROM scratch\nLABEL org.opencontainers.image.source=https://github.com/apple/containerization\n# empty add so that the build doesn't error due to no build directives\nADD . .\n"
  },
  {
    "path": "Tests/TestImages/emptyimage/Dockerfile",
    "content": "FROM scratch \nLABEL org.opencontainers.image.source=https://github.com/apple/containerization\nADD . .\n\n"
  },
  {
    "path": "examples/README.md",
    "content": "# Examples\n\nThis directory contains example projects demonstrating how to use Containerization.\n\n## Available Examples\n\n### [ctr-example](ctr-example/)\n\nA basic example of launching a Linux container using Containerization. This example demonstrates:\n- Fetching and configuring a Linux kernel\n- Creating and starting containers\n- Basic container management operations\n\nSee the [ctr-example README](ctr-example/README.md) for detailed build and run instructions.\n"
  },
  {
    "path": "examples/ctr-example/Makefile",
    "content": "# Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#   https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# ctr-example Makefile\n\nSWIFT = /usr/bin/swift\n\n.PHONY: all build clean run\n\nall: run\n\nbuild:\n\t$(SWIFT) build --configuration release\n\tcodesign --force --sign - --entitlements ctr-example.entitlements ./.build/release/ctr-example\n\tcp ./.build/release/ctr-example ./ctr-example\n\nclean:\n\t$(SWIFT) package clean\n\trm -f ./ctr-example\n\nrun: build\n\t./ctr-example\n\n# Development targets\ndebug:\n\t$(SWIFT) build\n\tcodesign --force --sign - --entitlements ctr-example.entitlements ./.build/debug/ctr-example\n\nfmt:\n\t$(SWIFT) format --in-place --recursive Sources/\nfetch-default-kernel:\n\t$(MAKE) -C ../.. fetch-default-kernel\n\tcp -L ../../.local/vmlinux ./vmlinux\n"
  },
  {
    "path": "examples/ctr-example/Package.resolved",
    "content": "{\n  \"originHash\" : \"5de11e9b526f881c570e7b65cb339765f3aa79e8646a0c1289d36f224f9f8ca0\",\n  \"pins\" : [\n    {\n      \"identity\" : \"async-http-client\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swift-server/async-http-client.git\",\n      \"state\" : {\n        \"revision\" : \"b2faff932b956df50668241d14f1b42f7bae12b4\",\n        \"version\" : \"1.30.0\"\n      }\n    },\n    {\n      \"identity\" : \"containerization\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/containerization.git\",\n      \"state\" : {\n        \"revision\" : \"636eef0eff00e451de6d5d426e6a6785b90b44e2\",\n        \"version\" : \"0.26.5\"\n      }\n    },\n    {\n      \"identity\" : \"grpc-swift\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/grpc/grpc-swift.git\",\n      \"state\" : {\n        \"revision\" : \"f857994e146f5146d702e9c31ac6f3c27d55d18a\",\n        \"version\" : \"1.27.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-algorithms\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-algorithms.git\",\n      \"state\" : {\n        \"revision\" : \"87e50f483c54e6efd60e885f7f5aa946cee68023\",\n        \"version\" : \"1.2.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-argument-parser\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-argument-parser.git\",\n      \"state\" : {\n        \"revision\" : \"cdd0ef3755280949551dc26dee5de9ddeda89f54\",\n        \"version\" : \"1.6.2\"\n      }\n    },\n    {\n      \"identity\" : \"swift-asn1\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-asn1.git\",\n      \"state\" : {\n        \"revision\" : \"40d25bbb2fc5b557a9aa8512210bded327c0f60d\",\n        \"version\" : \"1.5.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-async-algorithms\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-async-algorithms.git\",\n      \"state\" : {\n        \"revision\" : \"042e1c4d9d19748c9c228f8d4ebc97bb1e339b0b\",\n        \"version\" : \"1.0.4\"\n      }\n    },\n    {\n      \"identity\" : \"swift-atomics\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-atomics.git\",\n      \"state\" : {\n        \"revision\" : \"b601256eab081c0f92f059e12818ac1d4f178ff7\",\n        \"version\" : \"1.3.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-certificates\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-certificates.git\",\n      \"state\" : {\n        \"revision\" : \"c399f90e7bbe8874f6cbfda1d5f9023d1f5ce122\",\n        \"version\" : \"1.15.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-collections\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-collections.git\",\n      \"state\" : {\n        \"revision\" : \"7b847a3b7008b2dc2f47ca3110d8c782fb2e5c7e\",\n        \"version\" : \"1.3.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-crypto\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-crypto.git\",\n      \"state\" : {\n        \"revision\" : \"95ba0316a9b733e92bb6b071255ff46263bbe7dc\",\n        \"version\" : \"3.15.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-distributed-tracing\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-distributed-tracing.git\",\n      \"state\" : {\n        \"revision\" : \"baa932c1336f7894145cbaafcd34ce2dd0b77c97\",\n        \"version\" : \"1.3.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-http-structured-headers\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-http-structured-headers.git\",\n      \"state\" : {\n        \"revision\" : \"76d7627bd88b47bf5a0f8497dd244885960dde0b\",\n        \"version\" : \"1.6.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-http-types\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-http-types.git\",\n      \"state\" : {\n        \"revision\" : \"45eb0224913ea070ec4fba17291b9e7ecf4749ca\",\n        \"version\" : \"1.5.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-log\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-log.git\",\n      \"state\" : {\n        \"revision\" : \"ce592ae52f982c847a4efc0dd881cc9eb32d29f2\",\n        \"version\" : \"1.6.4\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio.git\",\n      \"state\" : {\n        \"revision\" : \"56724a2b6d8e2aed1b2c5f23865b9ea5c43f9977\",\n        \"version\" : \"2.89.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-extras\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-extras.git\",\n      \"state\" : {\n        \"revision\" : \"7ee281d816fa8e5f3967a2c294035a318ea551c7\",\n        \"version\" : \"1.31.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-http2\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-http2.git\",\n      \"state\" : {\n        \"revision\" : \"c2ba4cfbb83f307c66f5a6df6bb43e3c88dfbf80\",\n        \"version\" : \"1.39.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-ssl\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-ssl.git\",\n      \"state\" : {\n        \"revision\" : \"173cc69a058623525a58ae6710e2f5727c663793\",\n        \"version\" : \"2.36.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-transport-services\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-transport-services.git\",\n      \"state\" : {\n        \"revision\" : \"df6c28355051c72c884574a6c858bc54f7311ff9\",\n        \"version\" : \"1.25.2\"\n      }\n    },\n    {\n      \"identity\" : \"swift-numerics\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-numerics.git\",\n      \"state\" : {\n        \"revision\" : \"0c0290ff6b24942dadb83a929ffaaa1481df04a2\",\n        \"version\" : \"1.1.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-protobuf\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-protobuf.git\",\n      \"state\" : {\n        \"revision\" : \"c169a5744230951031770e27e475ff6eefe51f9d\",\n        \"version\" : \"1.33.3\"\n      }\n    },\n    {\n      \"identity\" : \"swift-service-context\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-service-context.git\",\n      \"state\" : {\n        \"revision\" : \"1983448fefc717a2bc2ebde5490fe99873c5b8a6\",\n        \"version\" : \"1.2.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-service-lifecycle\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swift-server/swift-service-lifecycle.git\",\n      \"state\" : {\n        \"revision\" : \"1de37290c0ab3c5a96028e0f02911b672fd42348\",\n        \"version\" : \"2.9.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-system\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-system.git\",\n      \"state\" : {\n        \"revision\" : \"395a77f0aa927f0ff73941d7ac35f2b46d47c9db\",\n        \"version\" : \"1.6.3\"\n      }\n    },\n    {\n      \"identity\" : \"zstd\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/facebook/zstd.git\",\n      \"state\" : {\n        \"revision\" : \"f8745da6ff1ad1e7bab384bd1f9d742439278e99\",\n        \"version\" : \"1.5.7\"\n      }\n    }\n  ],\n  \"version\" : 3\n}\n"
  },
  {
    "path": "examples/ctr-example/Package.swift",
    "content": "// swift-tools-version: 6.2\n//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport PackageDescription\n\nlet scVersion = \"0.26.5\"\n\nlet package = Package(\n    name: \"ctr-example\",\n    platforms: [\n        .macOS(\"26.0\")\n    ],\n    products: [\n        .executable(\n            name: \"ctr-example\",\n            targets: [\"ctr-example\"]\n        )\n    ],\n    dependencies: [\n        .package(url: \"https://github.com/apple/containerization.git\", exact: Version(stringLiteral: scVersion))\n    ],\n    targets: [\n        .executableTarget(\n            name: \"ctr-example\",\n            dependencies: [\n                .product(name: \"Containerization\", package: \"containerization\"),\n                .product(name: \"ContainerizationOS\", package: \"containerization\"),\n            ]\n        )\n    ]\n)\n"
  },
  {
    "path": "examples/ctr-example/README.md",
    "content": "# Container Example\n\nVery basic example of launching a Linux container using Containerization.\n\n## Build and Run\n\n### 1. Fetch Kernel\n\nIn your terminal, change directories to examples/ctr-example and run:\n\n**Option A: Using Makefile (recommended)**\n```bash\nmake fetch-default-kernel\n```\n\n**Option B: Copy from installed container tool**\n```bash\ncp \"$(ls -t ~/Library/Application\\ Support/com.apple.container/kernels/vmlinux-* | head -1)\" ./vmlinux\n```\n\nYou should now see the `vmlinux` image in examples/ctr-example\n\n### 2. Build/Run ctr-example\n\nFrom examples/ctr-example run\n`make all`\n\n> [!WARNING]\n> If you get the following error, try building from the default macOS terminal:\n> `error: compiled module was created by a newer version of the compiler`\n\nAfter the build completes, the example will run. In your terminal you should see something like:\n\n```\nStarting container example...\nFetching container initial filesystem...\nCreating container from docker.io/library/alpine:3.16...\nStarting container...\n/ #\n```\n\n> [!WARNING]\n> If you get the following error, try moving the `ctr-example` binary to `/var/tmp` and run it from there.\n> `Swift/ErrorType.swift:254: Fatal error: Error raised at top level: unsupported: \"failed to create vmnet network with status vmnet_return_t(rawValue: 1001)\"`\n\n**Congratulations, you've started the example container!**\n"
  },
  {
    "path": "examples/ctr-example/Sources/ctr-example/main.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Containerization\nimport ContainerizationOS\nimport Foundation\n\n@main\nstruct CtrExample {\n    static func main() async throws {\n        print(\"Starting container example...\")\n\n        // Set up terminal in raw mode (like cctl)\n        let current = try Terminal.current\n        try current.setraw()\n        defer { current.tryReset() }\n\n        let initfsReference = \"ghcr.io/apple/containerization/vminit:0.26.5\"\n        let kernelPath = \"./vmlinux\"\n        print(\"Fetching base container filesystem...\")\n        // Create container manager with file-based initfs\n        var manager = try await ContainerManager(\n            kernel: Kernel(path: URL(fileURLWithPath: kernelPath), platform: .linuxArm),\n            initfsReference: initfsReference,\n            network: try VmnetNetwork()\n        )\n\n        let containerId = \"ctr-example\"\n        let imageReference = \"docker.io/library/alpine:3.16\"\n\n        print(\"Creating container from \\(imageReference)...\")\n\n        // Create container with simple configuration\n        let container = try await manager.create(\n            containerId,\n            reference: imageReference,\n            rootfsSizeInBytes: 1.gib()\n        ) { @Sendable config in\n            config.cpus = 2\n            config.memoryInBytes = 512.mib()\n            config.process.setTerminalIO(terminal: current)\n            config.process.arguments = [\"/bin/sh\"]\n            config.process.workingDirectory = \"/\"\n        }\n\n        // Clean up on exit\n        defer {\n            try? manager.delete(containerId)\n        }\n\n        print(\"Starting container...\")\n        try await container.create()\n        try await container.start()\n\n        // Resize terminal to match current window\n        try? await container.resize(to: try current.size)\n\n        // Wait for container to finish\n        let exitCode = try await container.wait()\n\n        print(\"Container exited with code \\(exitCode)\")\n        try await container.stop()\n    }\n}\n"
  },
  {
    "path": "examples/ctr-example/ctr-example.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.virtualization</key>\n\t<true/>\n</dict>\n</plist>"
  },
  {
    "path": "examples/ctr-example/lab.md",
    "content": "#\n\n## Install and test the container tool:\n\nSee https://github.com/apple/container/releases\n\nOnce installed, start the service and follow prompts.\n\n```bash\ncontainer system start\n```\n\nThis'll install your kernel.\n\nAfter this start your first container. On first launch, this'll install another artifact for our guest init process:\n\n```\ncontainer run alpine uname\n```\n\nContainer starts after this will be fast!\n\n## Get the Containerization sources:\n\n```bash\n$ git clone https://github.com/apple/containerization.git\n```\n\n> [!IMPORTANT]\n> There is a bug in the `vmnet` framework on macOS 26 that causes network creation to fail if the creating applications are located under your `Documents` or `Desktop` directories. To workaround this, clone the project elsewhere, such as `~/projects/containerization`, until this issue is resolved.\n\n## Take a look at ctr-example\n\nRead through the sources:\n\n- ContainerManager:\n- manager.create()\n- container.create(), start(), wait(), stop()\n\n## Fetch the kernel\n\nRun \n\n```bash\ncp \"$(ls -t ~/Library/Application\\ Support/com.apple.container/kernels/vmlinux-* | head -1)\" ./vmlinux\n```\n\n## Build and run the example\n\n```bash\n$ cd examples/ctr-example\n$ make\n```\n\n## Modify the project\n\n- Change the command run by the container\n- Change the image\n"
  },
  {
    "path": "kernel/Makefile",
    "content": "# Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#   https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nKSOURCE ?= https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.18.5.tar.xz\nKIMAGE ?= kernel-build:0.1\nCURDIR := $(shell pwd)\n\n.DEFAULT_GOAL := all\n\n.PHONY: all\nall: kernel-build-image\nall: kernel-build\n\n.PHONY: kernel-build-image\nkernel-build-image:\n\tcontainer build image/ -f image/Dockerfile -t ${KIMAGE}\n\n.PHONY: kernel-build\nkernel-build:\nifeq (,$(wildcard source.tar.xz))\n\tcurl -SsL -o source.tar.xz ${KSOURCE}\nendif\n\tcontainer run \\\n\t\t--cpus 8 \\\n\t\t--rm \\\n\t\t--memory 16g \\\n\t\t-v ${CURDIR}:/kernel \\\n\t\t--cwd /kernel \\\n\t\t${KIMAGE} \\\n\t\t/bin/bash -c \"./build.sh\"\n"
  },
  {
    "path": "kernel/README.md",
    "content": "# Containerization Kernel Configuration\n\nThis directory includes an optimized kernel configuration to produce a fast and lightweight kernel for container use.\n\n- `config-arm64` includes the kernel `CONFIG_` options.\n- `Makefile` includes the kernel version and source package url.\n- `build.sh` scripts the kernel build process.\n- `image/` includes the configuration for an image with build tooling.\n\n## Building\n\n1. The build process relies on having the `container` tool installed (https://github.com/apple/container/releases).\n2. Run `make`. This should create the image used for building the resulting Linux kernel, and then run a container with that image to perform the kernel build.\n\nA `kernel/vmlinux` file will be the result of the build.\n"
  },
  {
    "path": "kernel/build.sh",
    "content": "#!/bin/bash\n# Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#   https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nset -e\n\nmkdir -p /kbuild\ntar -xf /kernel/source.tar.xz -C /kbuild --strip-components=1\ncp /kernel/config-arm64 /kbuild/.config\n\n(\n  cd /kbuild\n  make olddefconfig && \\\n    make -j$((`nproc`-1)) && \\\n    cp arch/arm64/boot/Image /kernel/vmlinux\n)\n"
  },
  {
    "path": "kernel/config-arm64",
    "content": "#\n# Automatically generated file; DO NOT EDIT.\n# Linux/arm64 6.1.68 Kernel Configuration\n#\nCONFIG_CC_VERSION_TEXT=\"gcc (containerization) 9.4.0\"\nCONFIG_CC_IS_GCC=y\nCONFIG_GCC_VERSION=90400\nCONFIG_CLANG_VERSION=0\nCONFIG_AS_IS_GNU=y\nCONFIG_AS_VERSION=23400\nCONFIG_LD_IS_BFD=y\nCONFIG_LD_VERSION=23400\nCONFIG_LLD_VERSION=0\nCONFIG_CC_CAN_LINK=y\nCONFIG_CC_CAN_LINK_STATIC=y\nCONFIG_CC_HAS_ASM_INLINE=y\nCONFIG_CC_HAS_NO_PROFILE_FN_ATTR=y\nCONFIG_PAHOLE_VERSION=0\nCONFIG_IRQ_WORK=y\nCONFIG_BUILDTIME_TABLE_SORT=y\nCONFIG_THREAD_INFO_IN_TASK=y\n\n#\n# General setup\n#\nCONFIG_INIT_ENV_ARG_LIMIT=32\n# CONFIG_COMPILE_TEST is not set\n# CONFIG_WERROR is not set\nCONFIG_LOCALVERSION=\"\"\n# CONFIG_LOCALVERSION_AUTO is not set\nCONFIG_BUILD_SALT=\"\"\nCONFIG_DEFAULT_INIT=\"\"\nCONFIG_DEFAULT_HOSTNAME=\"sandbox-vm\"\nCONFIG_SYSVIPC=y\nCONFIG_SYSVIPC_SYSCTL=y\nCONFIG_POSIX_MQUEUE=y\nCONFIG_POSIX_MQUEUE_SYSCTL=y\n# CONFIG_WATCH_QUEUE is not set\nCONFIG_CROSS_MEMORY_ATTACH=y\n# CONFIG_USELIB is not set\nCONFIG_AUDIT=y\nCONFIG_HAVE_ARCH_AUDITSYSCALL=y\nCONFIG_AUDITSYSCALL=y\n\n#\n# IRQ subsystem\n#\nCONFIG_GENERIC_IRQ_PROBE=y\nCONFIG_GENERIC_IRQ_SHOW=y\nCONFIG_GENERIC_IRQ_SHOW_LEVEL=y\nCONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y\nCONFIG_GENERIC_IRQ_MIGRATION=y\nCONFIG_HARDIRQS_SW_RESEND=y\nCONFIG_IRQ_DOMAIN=y\nCONFIG_IRQ_DOMAIN_HIERARCHY=y\nCONFIG_GENERIC_IRQ_IPI=y\nCONFIG_GENERIC_MSI_IRQ=y\nCONFIG_GENERIC_MSI_IRQ_DOMAIN=y\nCONFIG_IRQ_MSI_IOMMU=y\nCONFIG_IRQ_FORCED_THREADING=y\nCONFIG_SPARSE_IRQ=y\n# CONFIG_GENERIC_IRQ_DEBUGFS is not set\n# end of IRQ subsystem\n\nCONFIG_GENERIC_TIME_VSYSCALL=y\nCONFIG_GENERIC_CLOCKEVENTS=y\nCONFIG_ARCH_HAS_TICK_BROADCAST=y\nCONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y\nCONFIG_HAVE_POSIX_CPU_TIMERS_TASK_WORK=y\nCONFIG_POSIX_CPU_TIMERS_TASK_WORK=y\nCONFIG_CONTEXT_TRACKING=y\nCONFIG_CONTEXT_TRACKING_IDLE=y\n\n#\n# Timers subsystem\n#\nCONFIG_TICK_ONESHOT=y\nCONFIG_NO_HZ_COMMON=y\n# CONFIG_HZ_PERIODIC is not set\nCONFIG_NO_HZ_IDLE=y\n# CONFIG_NO_HZ_FULL is not set\nCONFIG_NO_HZ=y\nCONFIG_HIGH_RES_TIMERS=y\n# end of Timers subsystem\n\nCONFIG_BPF=y\nCONFIG_HAVE_EBPF_JIT=y\nCONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y\n\n#\n# BPF subsystem\n#\nCONFIG_BPF_SYSCALL=y\n# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set\nCONFIG_USERMODE_DRIVER=y\nCONFIG_BPF_PRELOAD=y\nCONFIG_BPF_PRELOAD_UMD=y\n# end of BPF subsystem\n\nCONFIG_PREEMPT_NONE_BUILD=y\nCONFIG_PREEMPT_NONE=y\n# CONFIG_PREEMPT_VOLUNTARY is not set\n# CONFIG_PREEMPT is not set\n# CONFIG_PREEMPT_DYNAMIC is not set\n# CONFIG_SCHED_CORE is not set\n\n#\n# CPU/Task time and stats accounting\n#\nCONFIG_TICK_CPU_ACCOUNTING=y\n# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set\n# CONFIG_IRQ_TIME_ACCOUNTING is not set\nCONFIG_HAVE_SCHED_AVG_IRQ=y\nCONFIG_BSD_PROCESS_ACCT=y\nCONFIG_BSD_PROCESS_ACCT_V3=y\nCONFIG_TASKSTATS=y\nCONFIG_TASK_DELAY_ACCT=y\nCONFIG_TASK_XACCT=y\nCONFIG_TASK_IO_ACCOUNTING=y\n# CONFIG_PSI is not set\n# end of CPU/Task time and stats accounting\n\nCONFIG_CPU_ISOLATION=y\n\n#\n# RCU Subsystem\n#\nCONFIG_TREE_RCU=y\n# CONFIG_RCU_EXPERT is not set\nCONFIG_SRCU=y\nCONFIG_TREE_SRCU=y\nCONFIG_TASKS_RCU_GENERIC=y\nCONFIG_TASKS_TRACE_RCU=y\nCONFIG_RCU_STALL_COMMON=y\nCONFIG_RCU_NEED_SEGCBLIST=y\n# end of RCU Subsystem\n\nCONFIG_IKCONFIG=y\nCONFIG_IKCONFIG_PROC=y\n# CONFIG_IKHEADERS is not set\nCONFIG_LOG_BUF_SHIFT=21\nCONFIG_LOG_CPU_MAX_BUF_SHIFT=12\nCONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13\n# CONFIG_PRINTK_INDEX is not set\nCONFIG_GENERIC_SCHED_CLOCK=y\n\n#\n# Scheduler features\n#\n# end of Scheduler features\n\nCONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y\nCONFIG_CC_HAS_INT128=y\nCONFIG_CC_IMPLICIT_FALLTHROUGH=\"-Wimplicit-fallthrough=5\"\nCONFIG_GCC11_NO_ARRAY_BOUNDS=y\nCONFIG_ARCH_SUPPORTS_INT128=y\nCONFIG_NUMA_BALANCING=y\n# CONFIG_NUMA_BALANCING_DEFAULT_ENABLED is not set\nCONFIG_CGROUPS=y\nCONFIG_PAGE_COUNTER=y\n# CONFIG_CGROUP_FAVOR_DYNMODS is not set\nCONFIG_MEMCG=y\nCONFIG_MEMCG_KMEM=y\nCONFIG_BLK_CGROUP=y\nCONFIG_CGROUP_WRITEBACK=y\nCONFIG_CGROUP_SCHED=y\nCONFIG_FAIR_GROUP_SCHED=y\nCONFIG_CFS_BANDWIDTH=y\nCONFIG_RT_GROUP_SCHED=y\nCONFIG_CGROUP_PIDS=y\n# CONFIG_CGROUP_RDMA is not set\nCONFIG_CGROUP_FREEZER=y\nCONFIG_CGROUP_HUGETLB=y\nCONFIG_CPUSETS=y\nCONFIG_PROC_PID_CPUSET=y\nCONFIG_CGROUP_DEVICE=y\nCONFIG_CGROUP_CPUACCT=y\nCONFIG_CGROUP_PERF=y\nCONFIG_CGROUP_BPF=y\n# CONFIG_CGROUP_MISC is not set\n# CONFIG_CGROUP_DEBUG is not set\nCONFIG_SOCK_CGROUP_DATA=y\nCONFIG_NAMESPACES=y\nCONFIG_UTS_NS=y\nCONFIG_TIME_NS=y\nCONFIG_IPC_NS=y\nCONFIG_USER_NS=y\nCONFIG_PID_NS=y\nCONFIG_NET_NS=y\n# CONFIG_CHECKPOINT_RESTORE is not set\nCONFIG_SCHED_AUTOGROUP=y\n# CONFIG_SYSFS_DEPRECATED is not set\nCONFIG_RELAY=y\nCONFIG_BLK_DEV_INITRD=y\nCONFIG_INITRAMFS_SOURCE=\"\"\nCONFIG_RD_GZIP=y\nCONFIG_RD_BZIP2=y\nCONFIG_RD_LZMA=y\nCONFIG_RD_XZ=y\nCONFIG_RD_LZO=y\nCONFIG_RD_LZ4=y\n# CONFIG_RD_ZSTD is not set\n# CONFIG_BOOT_CONFIG is not set\nCONFIG_INITRAMFS_PRESERVE_MTIME=y\nCONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y\n# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set\nCONFIG_LD_ORPHAN_WARN=y\nCONFIG_SYSCTL=y\nCONFIG_SYSCTL_EXCEPTION_TRACE=y\nCONFIG_EXPERT=y\nCONFIG_MULTIUSER=y\nCONFIG_SGETMASK_SYSCALL=y\nCONFIG_SYSFS_SYSCALL=y\nCONFIG_FHANDLE=y\nCONFIG_POSIX_TIMERS=y\nCONFIG_PRINTK=y\nCONFIG_BUG=y\nCONFIG_ELF_CORE=y\nCONFIG_BASE_FULL=y\nCONFIG_FUTEX=y\nCONFIG_FUTEX_PI=y\nCONFIG_EPOLL=y\nCONFIG_SIGNALFD=y\nCONFIG_TIMERFD=y\nCONFIG_EVENTFD=y\nCONFIG_SHMEM=y\nCONFIG_AIO=y\nCONFIG_IO_URING=y\nCONFIG_ADVISE_SYSCALLS=y\nCONFIG_MEMBARRIER=y\nCONFIG_KALLSYMS=y\n# CONFIG_KALLSYMS_ALL is not set\nCONFIG_KALLSYMS_BASE_RELATIVE=y\nCONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y\nCONFIG_KCMP=y\nCONFIG_RSEQ=y\n# CONFIG_DEBUG_RSEQ is not set\n# CONFIG_EMBEDDED is not set\nCONFIG_HAVE_PERF_EVENTS=y\nCONFIG_GUEST_PERF_EVENTS=y\n# CONFIG_PC104 is not set\n\n#\n# Kernel Performance Events And Counters\n#\nCONFIG_PERF_EVENTS=y\n# CONFIG_DEBUG_PERF_USE_VMALLOC is not set\n# end of Kernel Performance Events And Counters\n\n# CONFIG_PROFILING is not set\n# end of General setup\n\nCONFIG_ARM64=y\nCONFIG_GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_REGS=y\nCONFIG_64BIT=y\nCONFIG_MMU=y\nCONFIG_ARM64_PAGE_SHIFT=12\nCONFIG_ARM64_CONT_PTE_SHIFT=4\nCONFIG_ARM64_CONT_PMD_SHIFT=4\nCONFIG_ARCH_MMAP_RND_BITS_MIN=18\nCONFIG_ARCH_MMAP_RND_BITS_MAX=33\nCONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=11\nCONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16\nCONFIG_STACKTRACE_SUPPORT=y\nCONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000\nCONFIG_LOCKDEP_SUPPORT=y\nCONFIG_GENERIC_BUG=y\nCONFIG_GENERIC_BUG_RELATIVE_POINTERS=y\nCONFIG_GENERIC_HWEIGHT=y\nCONFIG_GENERIC_CSUM=y\nCONFIG_GENERIC_CALIBRATE_DELAY=y\nCONFIG_ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE=y\nCONFIG_SMP=y\nCONFIG_KERNEL_MODE_NEON=y\nCONFIG_FIX_EARLYCON_MEM=y\nCONFIG_PGTABLE_LEVELS=4\nCONFIG_ARCH_SUPPORTS_UPROBES=y\nCONFIG_ARCH_PROC_KCORE_TEXT=y\n\n#\n# Platform selection\n#\n# CONFIG_ARCH_ACTIONS is not set\n# CONFIG_ARCH_SUNXI is not set\n# CONFIG_ARCH_ALPINE is not set\n# CONFIG_ARCH_APPLE is not set\n# CONFIG_ARCH_BCM is not set\n# CONFIG_ARCH_BERLIN is not set\n# CONFIG_ARCH_BITMAIN is not set\n# CONFIG_ARCH_EXYNOS is not set\n# CONFIG_ARCH_SPARX5 is not set\n# CONFIG_ARCH_K3 is not set\n# CONFIG_ARCH_LG1K is not set\n# CONFIG_ARCH_HISI is not set\n# CONFIG_ARCH_KEEMBAY is not set\n# CONFIG_ARCH_MEDIATEK is not set\n# CONFIG_ARCH_MESON is not set\n# CONFIG_ARCH_MVEBU is not set\n# CONFIG_ARCH_NXP is not set\n# CONFIG_ARCH_NPCM is not set\n# CONFIG_ARCH_QCOM is not set\n# CONFIG_ARCH_REALTEK is not set\n# CONFIG_ARCH_RENESAS is not set\n# CONFIG_ARCH_ROCKCHIP is not set\n# CONFIG_ARCH_SEATTLE is not set\n# CONFIG_ARCH_INTEL_SOCFPGA is not set\n# CONFIG_ARCH_SYNQUACER is not set\n# CONFIG_ARCH_TEGRA is not set\n# CONFIG_ARCH_SPRD is not set\n# CONFIG_ARCH_THUNDER is not set\n# CONFIG_ARCH_THUNDER2 is not set\n# CONFIG_ARCH_UNIPHIER is not set\n# CONFIG_ARCH_VEXPRESS is not set\n# CONFIG_ARCH_VISCONTI is not set\n# CONFIG_ARCH_XGENE is not set\n# CONFIG_ARCH_ZYNQMP is not set\n# end of Platform selection\n\n#\n# Kernel Features\n#\n\n#\n# ARM errata workarounds via the alternatives framework\n#\nCONFIG_AMPERE_ERRATUM_AC03_CPU_38=y\nCONFIG_ARM64_WORKAROUND_CLEAN_CACHE=y\nCONFIG_ARM64_ERRATUM_826319=y\nCONFIG_ARM64_ERRATUM_827319=y\nCONFIG_ARM64_ERRATUM_824069=y\nCONFIG_ARM64_ERRATUM_819472=y\nCONFIG_ARM64_ERRATUM_832075=y\nCONFIG_ARM64_ERRATUM_834220=y\nCONFIG_ARM64_ERRATUM_843419=y\nCONFIG_ARM64_LD_HAS_FIX_ERRATUM_843419=y\nCONFIG_ARM64_ERRATUM_1024718=y\nCONFIG_ARM64_WORKAROUND_SPECULATIVE_AT=y\nCONFIG_ARM64_ERRATUM_1165522=y\nCONFIG_ARM64_ERRATUM_1319367=y\nCONFIG_ARM64_ERRATUM_1530923=y\nCONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y\nCONFIG_ARM64_ERRATUM_2441007=y\nCONFIG_ARM64_ERRATUM_1286807=y\nCONFIG_ARM64_ERRATUM_1463225=y\nCONFIG_ARM64_ERRATUM_1542419=y\nCONFIG_ARM64_ERRATUM_1508412=y\nCONFIG_ARM64_ERRATUM_2051678=y\nCONFIG_ARM64_ERRATUM_2077057=y\nCONFIG_ARM64_ERRATUM_2658417=y\nCONFIG_ARM64_WORKAROUND_TSB_FLUSH_FAILURE=y\nCONFIG_ARM64_ERRATUM_2054223=y\nCONFIG_ARM64_ERRATUM_2067961=y\nCONFIG_ARM64_ERRATUM_2441009=y\nCONFIG_ARM64_ERRATUM_2457168=y\nCONFIG_ARM64_ERRATUM_2966298=y\nCONFIG_CAVIUM_ERRATUM_22375=y\nCONFIG_CAVIUM_ERRATUM_23144=y\nCONFIG_CAVIUM_ERRATUM_23154=y\nCONFIG_CAVIUM_ERRATUM_27456=y\nCONFIG_CAVIUM_ERRATUM_30115=y\nCONFIG_CAVIUM_TX2_ERRATUM_219=y\nCONFIG_FUJITSU_ERRATUM_010001=y\nCONFIG_HISILICON_ERRATUM_161600802=y\nCONFIG_QCOM_FALKOR_ERRATUM_1003=y\nCONFIG_QCOM_FALKOR_ERRATUM_1009=y\nCONFIG_QCOM_QDF2400_ERRATUM_0065=y\nCONFIG_QCOM_FALKOR_ERRATUM_E1041=y\n# CONFIG_NVIDIA_CARMEL_CNP_ERRATUM is not set\nCONFIG_SOCIONEXT_SYNQUACER_PREITS=y\n# end of ARM errata workarounds via the alternatives framework\n\nCONFIG_ARM64_4K_PAGES=y\n# CONFIG_ARM64_16K_PAGES is not set\n# CONFIG_ARM64_64K_PAGES is not set\n# CONFIG_ARM64_VA_BITS_39 is not set\nCONFIG_ARM64_VA_BITS_48=y\nCONFIG_ARM64_VA_BITS=48\nCONFIG_ARM64_PA_BITS_48=y\nCONFIG_ARM64_PA_BITS=48\n# CONFIG_CPU_BIG_ENDIAN is not set\nCONFIG_CPU_LITTLE_ENDIAN=y\nCONFIG_SCHED_MC=y\n# CONFIG_SCHED_CLUSTER is not set\nCONFIG_SCHED_SMT=y\nCONFIG_NR_CPUS=128\nCONFIG_HOTPLUG_CPU=y\nCONFIG_NUMA=y\nCONFIG_NODES_SHIFT=10\n# CONFIG_HZ_100 is not set\nCONFIG_HZ_250=y\n# CONFIG_HZ_300 is not set\n# CONFIG_HZ_1000 is not set\nCONFIG_HZ=250\nCONFIG_SCHED_HRTICK=y\nCONFIG_ARCH_SPARSEMEM_ENABLE=y\nCONFIG_HW_PERF_EVENTS=y\nCONFIG_PARAVIRT=y\nCONFIG_PARAVIRT_TIME_ACCOUNTING=y\n# CONFIG_KEXEC is not set\nCONFIG_KEXEC_FILE=y\n# CONFIG_KEXEC_SIG is not set\n# CONFIG_CRASH_DUMP is not set\nCONFIG_TRANS_TABLE=y\n# CONFIG_XEN is not set\nCONFIG_ARCH_FORCE_MAX_ORDER=11\nCONFIG_UNMAP_KERNEL_AT_EL0=y\nCONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY=y\nCONFIG_RODATA_FULL_DEFAULT_ENABLED=y\n# CONFIG_ARM64_SW_TTBR0_PAN is not set\nCONFIG_ARM64_TAGGED_ADDR_ABI=y\n# CONFIG_COMPAT is not set\n\n#\n# ARMv8.1 architectural features\n#\nCONFIG_ARM64_HW_AFDBM=y\nCONFIG_ARM64_PAN=y\nCONFIG_AS_HAS_LDAPR=y\nCONFIG_AS_HAS_LSE_ATOMICS=y\nCONFIG_ARM64_LSE_ATOMICS=y\nCONFIG_ARM64_USE_LSE_ATOMICS=y\n# end of ARMv8.1 architectural features\n\n#\n# ARMv8.2 architectural features\n#\nCONFIG_AS_HAS_ARMV8_2=y\nCONFIG_AS_HAS_SHA3=y\n# CONFIG_ARM64_PMEM is not set\nCONFIG_ARM64_RAS_EXTN=y\nCONFIG_ARM64_CNP=y\n# end of ARMv8.2 architectural features\n\n#\n# ARMv8.3 architectural features\n#\nCONFIG_ARM64_PTR_AUTH=y\nCONFIG_ARM64_PTR_AUTH_KERNEL=y\nCONFIG_CC_HAS_BRANCH_PROT_PAC_RET=y\nCONFIG_CC_HAS_SIGN_RETURN_ADDRESS=y\nCONFIG_AS_HAS_PAC=y\nCONFIG_AS_HAS_CFI_NEGATE_RA_STATE=y\n# end of ARMv8.3 architectural features\n\n#\n# ARMv8.4 architectural features\n#\nCONFIG_ARM64_AMU_EXTN=y\nCONFIG_AS_HAS_ARMV8_4=y\nCONFIG_ARM64_TLB_RANGE=y\n# end of ARMv8.4 architectural features\n\n#\n# ARMv8.5 architectural features\n#\nCONFIG_AS_HAS_ARMV8_5=y\nCONFIG_ARM64_BTI=y\nCONFIG_CC_HAS_BRANCH_PROT_PAC_RET_BTI=y\nCONFIG_ARM64_E0PD=y\nCONFIG_ARM64_AS_HAS_MTE=y\nCONFIG_ARM64_MTE=y\n# end of ARMv8.5 architectural features\n\n#\n# ARMv8.7 architectural features\n#\nCONFIG_ARM64_EPAN=y\n# end of ARMv8.7 architectural features\n\n# CONFIG_ARM64_SVE is not set\n# CONFIG_ARM64_PSEUDO_NMI is not set\nCONFIG_RELOCATABLE=y\nCONFIG_RANDOMIZE_BASE=y\nCONFIG_RANDOMIZE_MODULE_REGION_FULL=y\nCONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y\nCONFIG_STACKPROTECTOR_PER_TASK=y\nCONFIG_ARCH_NR_GPIO=0\n# end of Kernel Features\n\n#\n# Boot options\n#\n# CONFIG_ARM64_ACPI_PARKING_PROTOCOL is not set\nCONFIG_CMDLINE=\"\"\nCONFIG_EFI_STUB=y\nCONFIG_EFI=y\nCONFIG_DMI=y\n# end of Boot options\n\n#\n# Power management options\n#\n# CONFIG_SUSPEND is not set\nCONFIG_HIBERNATE_CALLBACKS=y\nCONFIG_HIBERNATION=y\nCONFIG_HIBERNATION_SNAPSHOT_DEV=y\nCONFIG_PM_STD_PARTITION=\"\"\nCONFIG_PM_SLEEP=y\nCONFIG_PM_SLEEP_SMP=y\n# CONFIG_PM_AUTOSLEEP is not set\n# CONFIG_PM_USERSPACE_AUTOSLEEP is not set\n# CONFIG_PM_WAKELOCKS is not set\nCONFIG_PM=y\n# CONFIG_PM_DEBUG is not set\nCONFIG_PM_CLK=y\n# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set\nCONFIG_CPU_PM=y\n# CONFIG_ENERGY_MODEL is not set\nCONFIG_ARCH_HIBERNATION_POSSIBLE=y\nCONFIG_ARCH_HIBERNATION_HEADER=y\nCONFIG_ARCH_SUSPEND_POSSIBLE=y\n# end of Power management options\n\n#\n# CPU Power Management\n#\n\n#\n# CPU Idle\n#\nCONFIG_CPU_IDLE=y\nCONFIG_CPU_IDLE_GOV_LADDER=y\nCONFIG_CPU_IDLE_GOV_MENU=y\n# CONFIG_CPU_IDLE_GOV_TEO is not set\n\n#\n# ARM CPU Idle Drivers\n#\n# CONFIG_ARM_PSCI_CPUIDLE is not set\n# end of ARM CPU Idle Drivers\n# end of CPU Idle\n\n#\n# CPU Frequency scaling\n#\nCONFIG_CPU_FREQ=y\n# CONFIG_CPU_FREQ_STAT is not set\nCONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y\n# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set\n# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set\n# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set\n# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set\n# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set\nCONFIG_CPU_FREQ_GOV_PERFORMANCE=y\n# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set\n# CONFIG_CPU_FREQ_GOV_USERSPACE is not set\n# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set\n# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set\n# CONFIG_CPU_FREQ_GOV_SCHEDUTIL is not set\n\n#\n# CPU frequency scaling drivers\n#\n# CONFIG_CPUFREQ_DT is not set\n# CONFIG_ACPI_CPPC_CPUFREQ is not set\n# end of CPU Frequency scaling\n# end of CPU Power Management\n\nCONFIG_ARCH_SUPPORTS_ACPI=y\nCONFIG_ACPI=y\nCONFIG_ACPI_GENERIC_GSI=y\nCONFIG_ACPI_CCA_REQUIRED=y\n# CONFIG_ACPI_DEBUGGER is not set\nCONFIG_ACPI_SPCR_TABLE=y\n# CONFIG_ACPI_EC_DEBUGFS is not set\n# CONFIG_ACPI_AC is not set\n# CONFIG_ACPI_BATTERY is not set\nCONFIG_ACPI_BUTTON=y\n# CONFIG_ACPI_VIDEO is not set\n# CONFIG_ACPI_FAN is not set\n# CONFIG_ACPI_TAD is not set\n# CONFIG_ACPI_DOCK is not set\nCONFIG_ACPI_PROCESSOR_IDLE=y\nCONFIG_ACPI_MCFG=y\nCONFIG_ACPI_PROCESSOR=y\nCONFIG_ACPI_HOTPLUG_CPU=y\nCONFIG_ACPI_THERMAL=y\nCONFIG_ARCH_HAS_ACPI_TABLE_UPGRADE=y\nCONFIG_ACPI_TABLE_UPGRADE=y\n# CONFIG_ACPI_DEBUG is not set\n# CONFIG_ACPI_PCI_SLOT is not set\nCONFIG_ACPI_CONTAINER=y\nCONFIG_ACPI_HOTPLUG_MEMORY=y\n# CONFIG_ACPI_HED is not set\n# CONFIG_ACPI_CUSTOM_METHOD is not set\n# CONFIG_ACPI_BGRT is not set\nCONFIG_ACPI_REDUCED_HARDWARE_ONLY=y\nCONFIG_ACPI_NUMA=y\n# CONFIG_ACPI_HMAT is not set\nCONFIG_HAVE_ACPI_APEI=y\n# CONFIG_ACPI_APEI is not set\n# CONFIG_ACPI_CONFIGFS is not set\n# CONFIG_ACPI_PFRUT is not set\nCONFIG_ACPI_IORT=y\nCONFIG_ACPI_GTDT=y\nCONFIG_ACPI_PPTT=y\nCONFIG_ACPI_PCC=y\n# CONFIG_PMIC_OPREGION is not set\nCONFIG_ACPI_VIOT=y\nCONFIG_ACPI_PRMT=y\nCONFIG_IRQ_BYPASS_MANAGER=y\nCONFIG_HAVE_KVM=y\nCONFIG_HAVE_KVM_IRQCHIP=y\nCONFIG_HAVE_KVM_IRQFD=y\nCONFIG_HAVE_KVM_IRQ_ROUTING=y\nCONFIG_HAVE_KVM_EVENTFD=y\nCONFIG_KVM_MMIO=y\nCONFIG_HAVE_KVM_MSI=y\nCONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT=y\nCONFIG_KVM_VFIO=y\nCONFIG_HAVE_KVM_ARCH_TLB_FLUSH_ALL=y\nCONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT=y\nCONFIG_HAVE_KVM_IRQ_BYPASS=y\nCONFIG_HAVE_KVM_VCPU_RUN_PID_CHANGE=y\nCONFIG_KVM_XFER_TO_GUEST_WORK=y\nCONFIG_VIRTUALIZATION=y\nCONFIG_KVM=y\n# CONFIG_NVHE_EL2_DEBUG is not set\n\n#\n# General architecture-dependent options\n#\nCONFIG_CRASH_CORE=y\nCONFIG_KEXEC_CORE=y\nCONFIG_ARCH_HAS_SUBPAGE_FAULTS=y\nCONFIG_JUMP_LABEL=y\n# CONFIG_STATIC_KEYS_SELFTEST is not set\nCONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y\nCONFIG_HAVE_IOREMAP_PROT=y\nCONFIG_HAVE_KPROBES=y\nCONFIG_HAVE_KRETPROBES=y\nCONFIG_ARCH_CORRECT_STACKTRACE_ON_KRETPROBE=y\nCONFIG_HAVE_FUNCTION_ERROR_INJECTION=y\nCONFIG_HAVE_NMI=y\nCONFIG_TRACE_IRQFLAGS_SUPPORT=y\nCONFIG_TRACE_IRQFLAGS_NMI_SUPPORT=y\nCONFIG_HAVE_ARCH_TRACEHOOK=y\nCONFIG_HAVE_DMA_CONTIGUOUS=y\nCONFIG_GENERIC_SMP_IDLE_THREAD=y\nCONFIG_GENERIC_IDLE_POLL_SETUP=y\nCONFIG_ARCH_HAS_FORTIFY_SOURCE=y\nCONFIG_ARCH_HAS_KEEPINITRD=y\nCONFIG_ARCH_HAS_SET_MEMORY=y\nCONFIG_ARCH_HAS_SET_DIRECT_MAP=y\nCONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y\nCONFIG_ARCH_WANTS_NO_INSTR=y\nCONFIG_HAVE_ASM_MODVERSIONS=y\nCONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y\nCONFIG_HAVE_RSEQ=y\nCONFIG_HAVE_FUNCTION_ARG_ACCESS_API=y\nCONFIG_HAVE_HW_BREAKPOINT=y\nCONFIG_HAVE_PERF_REGS=y\nCONFIG_HAVE_PERF_USER_STACK_DUMP=y\nCONFIG_HAVE_ARCH_JUMP_LABEL=y\nCONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE=y\nCONFIG_MMU_GATHER_TABLE_FREE=y\nCONFIG_MMU_GATHER_RCU_TABLE_FREE=y\nCONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y\nCONFIG_HAVE_ALIGNED_STRUCT_PAGE=y\nCONFIG_HAVE_CMPXCHG_LOCAL=y\nCONFIG_HAVE_CMPXCHG_DOUBLE=y\nCONFIG_HAVE_ARCH_SECCOMP=y\nCONFIG_HAVE_ARCH_SECCOMP_FILTER=y\nCONFIG_SECCOMP=y\nCONFIG_SECCOMP_FILTER=y\n# CONFIG_SECCOMP_CACHE_DEBUG is not set\nCONFIG_HAVE_ARCH_STACKLEAK=y\nCONFIG_HAVE_STACKPROTECTOR=y\nCONFIG_STACKPROTECTOR=y\nCONFIG_STACKPROTECTOR_STRONG=y\nCONFIG_ARCH_SUPPORTS_LTO_CLANG=y\nCONFIG_ARCH_SUPPORTS_LTO_CLANG_THIN=y\nCONFIG_LTO_NONE=y\nCONFIG_ARCH_SUPPORTS_CFI_CLANG=y\nCONFIG_HAVE_CONTEXT_TRACKING_USER=y\nCONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y\nCONFIG_HAVE_IRQ_TIME_ACCOUNTING=y\nCONFIG_HAVE_MOVE_PUD=y\nCONFIG_HAVE_MOVE_PMD=y\nCONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y\nCONFIG_HAVE_ARCH_HUGE_VMAP=y\nCONFIG_HAVE_ARCH_HUGE_VMALLOC=y\nCONFIG_ARCH_WANT_HUGE_PMD_SHARE=y\nCONFIG_MODULES_USE_ELF_RELA=y\nCONFIG_HAVE_SOFTIRQ_ON_OWN_STACK=y\nCONFIG_SOFTIRQ_ON_OWN_STACK=y\nCONFIG_ARCH_HAS_ELF_RANDOMIZE=y\nCONFIG_HAVE_ARCH_MMAP_RND_BITS=y\nCONFIG_ARCH_MMAP_RND_BITS=18\nCONFIG_PAGE_SIZE_LESS_THAN_64KB=y\nCONFIG_PAGE_SIZE_LESS_THAN_256KB=y\nCONFIG_ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT=y\nCONFIG_CLONE_BACKWARDS=y\n# CONFIG_COMPAT_32BIT_TIME is not set\nCONFIG_HAVE_ARCH_VMAP_STACK=y\nCONFIG_VMAP_STACK=y\nCONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET=y\nCONFIG_RANDOMIZE_KSTACK_OFFSET=y\n# CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT is not set\nCONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y\nCONFIG_STRICT_KERNEL_RWX=y\nCONFIG_ARCH_HAS_STRICT_MODULE_RWX=y\nCONFIG_HAVE_ARCH_COMPILER_H=y\nCONFIG_HAVE_ARCH_PREL32_RELOCATIONS=y\nCONFIG_ARCH_USE_MEMREMAP_PROT=y\n# CONFIG_LOCK_EVENT_COUNTS is not set\nCONFIG_ARCH_HAS_RELR=y\nCONFIG_HAVE_PREEMPT_DYNAMIC=y\nCONFIG_HAVE_PREEMPT_DYNAMIC_KEY=y\nCONFIG_ARCH_WANT_LD_ORPHAN_WARN=y\nCONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y\nCONFIG_ARCH_SUPPORTS_PAGE_TABLE_CHECK=y\nCONFIG_ARCH_HAVE_TRACE_MMIO_ACCESS=y\n\n#\n# GCOV-based kernel profiling\n#\n# CONFIG_GCOV_KERNEL is not set\nCONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y\n# end of GCOV-based kernel profiling\n\nCONFIG_HAVE_GCC_PLUGINS=y\n# end of General architecture-dependent options\n\nCONFIG_RT_MUTEXES=y\nCONFIG_BASE_SMALL=0\n# CONFIG_MODULES is not set\nCONFIG_BLOCK=y\nCONFIG_BLOCK_LEGACY_AUTOLOAD=y\nCONFIG_BLK_CGROUP_RWSTAT=y\nCONFIG_BLK_DEV_BSG_COMMON=y\nCONFIG_BLK_DEV_BSGLIB=y\nCONFIG_BLK_DEV_INTEGRITY=y\n# CONFIG_BLK_DEV_ZONED is not set\nCONFIG_BLK_DEV_THROTTLING=y\n# CONFIG_BLK_DEV_THROTTLING_LOW is not set\nCONFIG_BLK_WBT=y\nCONFIG_BLK_WBT_MQ=y\n# CONFIG_BLK_CGROUP_IOLATENCY is not set\n# CONFIG_BLK_CGROUP_IOCOST is not set\n# CONFIG_BLK_CGROUP_IOPRIO is not set\nCONFIG_BLK_DEBUG_FS=y\n# CONFIG_BLK_SED_OPAL is not set\n# CONFIG_BLK_INLINE_ENCRYPTION is not set\n\n#\n# Partition Types\n#\nCONFIG_PARTITION_ADVANCED=y\n# CONFIG_ACORN_PARTITION is not set\n# CONFIG_AIX_PARTITION is not set\n# CONFIG_OSF_PARTITION is not set\n# CONFIG_AMIGA_PARTITION is not set\n# CONFIG_ATARI_PARTITION is not set\n# CONFIG_MAC_PARTITION is not set\n# CONFIG_MSDOS_PARTITION is not set\n# CONFIG_LDM_PARTITION is not set\n# CONFIG_SGI_PARTITION is not set\n# CONFIG_ULTRIX_PARTITION is not set\n# CONFIG_SUN_PARTITION is not set\n# CONFIG_KARMA_PARTITION is not set\nCONFIG_EFI_PARTITION=y\n# CONFIG_SYSV68_PARTITION is not set\n# CONFIG_CMDLINE_PARTITION is not set\n# end of Partition Types\n\nCONFIG_BLK_MQ_PCI=y\nCONFIG_BLK_MQ_VIRTIO=y\nCONFIG_BLK_PM=y\n\n#\n# IO Schedulers\n#\n# CONFIG_MQ_IOSCHED_DEADLINE is not set\n# CONFIG_MQ_IOSCHED_KYBER is not set\n# CONFIG_IOSCHED_BFQ is not set\n# end of IO Schedulers\n\nCONFIG_PREEMPT_NOTIFIERS=y\nCONFIG_ASN1=y\nCONFIG_ARCH_INLINE_SPIN_TRYLOCK=y\nCONFIG_ARCH_INLINE_SPIN_TRYLOCK_BH=y\nCONFIG_ARCH_INLINE_SPIN_LOCK=y\nCONFIG_ARCH_INLINE_SPIN_LOCK_BH=y\nCONFIG_ARCH_INLINE_SPIN_LOCK_IRQ=y\nCONFIG_ARCH_INLINE_SPIN_LOCK_IRQSAVE=y\nCONFIG_ARCH_INLINE_SPIN_UNLOCK=y\nCONFIG_ARCH_INLINE_SPIN_UNLOCK_BH=y\nCONFIG_ARCH_INLINE_SPIN_UNLOCK_IRQ=y\nCONFIG_ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE=y\nCONFIG_ARCH_INLINE_READ_LOCK=y\nCONFIG_ARCH_INLINE_READ_LOCK_BH=y\nCONFIG_ARCH_INLINE_READ_LOCK_IRQ=y\nCONFIG_ARCH_INLINE_READ_LOCK_IRQSAVE=y\nCONFIG_ARCH_INLINE_READ_UNLOCK=y\nCONFIG_ARCH_INLINE_READ_UNLOCK_BH=y\nCONFIG_ARCH_INLINE_READ_UNLOCK_IRQ=y\nCONFIG_ARCH_INLINE_READ_UNLOCK_IRQRESTORE=y\nCONFIG_ARCH_INLINE_WRITE_LOCK=y\nCONFIG_ARCH_INLINE_WRITE_LOCK_BH=y\nCONFIG_ARCH_INLINE_WRITE_LOCK_IRQ=y\nCONFIG_ARCH_INLINE_WRITE_LOCK_IRQSAVE=y\nCONFIG_ARCH_INLINE_WRITE_UNLOCK=y\nCONFIG_ARCH_INLINE_WRITE_UNLOCK_BH=y\nCONFIG_ARCH_INLINE_WRITE_UNLOCK_IRQ=y\nCONFIG_ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE=y\nCONFIG_INLINE_SPIN_TRYLOCK=y\nCONFIG_INLINE_SPIN_TRYLOCK_BH=y\nCONFIG_INLINE_SPIN_LOCK=y\nCONFIG_INLINE_SPIN_LOCK_BH=y\nCONFIG_INLINE_SPIN_LOCK_IRQ=y\nCONFIG_INLINE_SPIN_LOCK_IRQSAVE=y\nCONFIG_INLINE_SPIN_UNLOCK_BH=y\nCONFIG_INLINE_SPIN_UNLOCK_IRQ=y\nCONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE=y\nCONFIG_INLINE_READ_LOCK=y\nCONFIG_INLINE_READ_LOCK_BH=y\nCONFIG_INLINE_READ_LOCK_IRQ=y\nCONFIG_INLINE_READ_LOCK_IRQSAVE=y\nCONFIG_INLINE_READ_UNLOCK=y\nCONFIG_INLINE_READ_UNLOCK_BH=y\nCONFIG_INLINE_READ_UNLOCK_IRQ=y\nCONFIG_INLINE_READ_UNLOCK_IRQRESTORE=y\nCONFIG_INLINE_WRITE_LOCK=y\nCONFIG_INLINE_WRITE_LOCK_BH=y\nCONFIG_INLINE_WRITE_LOCK_IRQ=y\nCONFIG_INLINE_WRITE_LOCK_IRQSAVE=y\nCONFIG_INLINE_WRITE_UNLOCK=y\nCONFIG_INLINE_WRITE_UNLOCK_BH=y\nCONFIG_INLINE_WRITE_UNLOCK_IRQ=y\nCONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE=y\nCONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y\nCONFIG_MUTEX_SPIN_ON_OWNER=y\nCONFIG_RWSEM_SPIN_ON_OWNER=y\nCONFIG_LOCK_SPIN_ON_OWNER=y\nCONFIG_ARCH_USE_QUEUED_SPINLOCKS=y\nCONFIG_QUEUED_SPINLOCKS=y\nCONFIG_ARCH_USE_QUEUED_RWLOCKS=y\nCONFIG_QUEUED_RWLOCKS=y\nCONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE=y\nCONFIG_ARCH_HAS_SYSCALL_WRAPPER=y\nCONFIG_FREEZER=y\n\n#\n# Executable file formats\n#\nCONFIG_BINFMT_ELF=y\nCONFIG_ARCH_BINFMT_ELF_STATE=y\nCONFIG_ARCH_BINFMT_ELF_EXTRA_PHDRS=y\nCONFIG_ARCH_HAVE_ELF_PROT=y\nCONFIG_ARCH_USE_GNU_PROPERTY=y\nCONFIG_ELFCORE=y\nCONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y\nCONFIG_BINFMT_SCRIPT=y\nCONFIG_BINFMT_MISC=y\nCONFIG_COREDUMP=y\n# end of Executable file formats\n\n#\n# Memory Management options\n#\nCONFIG_ZPOOL=y\nCONFIG_SWAP=y\nCONFIG_ZSWAP=y\n# CONFIG_ZSWAP_DEFAULT_ON is not set\n# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_DEFLATE is not set\nCONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZO=y\n# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_842 is not set\n# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZ4 is not set\n# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_LZ4HC is not set\n# CONFIG_ZSWAP_COMPRESSOR_DEFAULT_ZSTD is not set\nCONFIG_ZSWAP_COMPRESSOR_DEFAULT=\"lzo\"\nCONFIG_ZSWAP_ZPOOL_DEFAULT_ZBUD=y\n# CONFIG_ZSWAP_ZPOOL_DEFAULT_Z3FOLD is not set\n# CONFIG_ZSWAP_ZPOOL_DEFAULT_ZSMALLOC is not set\nCONFIG_ZSWAP_ZPOOL_DEFAULT=\"zbud\"\nCONFIG_ZBUD=y\n# CONFIG_Z3FOLD is not set\nCONFIG_ZSMALLOC=y\nCONFIG_ZSMALLOC_STAT=y\n\n#\n# SLAB allocator options\n#\n# CONFIG_SLAB is not set\nCONFIG_SLUB=y\n# CONFIG_SLOB is not set\nCONFIG_SLAB_MERGE_DEFAULT=y\n# CONFIG_SLAB_FREELIST_RANDOM is not set\nCONFIG_SLAB_FREELIST_HARDENED=y\n# CONFIG_SLUB_STATS is not set\nCONFIG_SLUB_CPU_PARTIAL=y\n# end of SLAB allocator options\n\n# CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set\n# CONFIG_COMPAT_BRK is not set\nCONFIG_SPARSEMEM=y\nCONFIG_SPARSEMEM_EXTREME=y\nCONFIG_SPARSEMEM_VMEMMAP_ENABLE=y\nCONFIG_SPARSEMEM_VMEMMAP=y\nCONFIG_HAVE_FAST_GUP=y\nCONFIG_ARCH_KEEP_MEMBLOCK=y\nCONFIG_NUMA_KEEP_MEMINFO=y\nCONFIG_MEMORY_ISOLATION=y\nCONFIG_EXCLUSIVE_SYSTEM_RAM=y\nCONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y\nCONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y\nCONFIG_MEMORY_HOTPLUG=y\n# CONFIG_MEMORY_HOTPLUG_DEFAULT_ONLINE is not set\nCONFIG_MEMORY_HOTREMOVE=y\nCONFIG_MHP_MEMMAP_ON_MEMORY=y\nCONFIG_SPLIT_PTLOCK_CPUS=4\nCONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y\nCONFIG_MEMORY_BALLOON=y\nCONFIG_BALLOON_COMPACTION=y\nCONFIG_COMPACTION=y\nCONFIG_COMPACT_UNEVICTABLE_DEFAULT=1\nCONFIG_PAGE_REPORTING=y\nCONFIG_MIGRATION=y\nCONFIG_DEVICE_MIGRATION=y\nCONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION=y\nCONFIG_ARCH_ENABLE_THP_MIGRATION=y\nCONFIG_CONTIG_ALLOC=y\nCONFIG_PHYS_ADDR_T_64BIT=y\nCONFIG_MMU_NOTIFIER=y\nCONFIG_KSM=y\nCONFIG_DEFAULT_MMAP_MIN_ADDR=4096\nCONFIG_ARCH_SUPPORTS_MEMORY_FAILURE=y\n# CONFIG_MEMORY_FAILURE is not set\nCONFIG_ARCH_WANTS_THP_SWAP=y\nCONFIG_TRANSPARENT_HUGEPAGE=y\n# CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS is not set\nCONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y\nCONFIG_THP_SWAP=y\n# CONFIG_READ_ONLY_THP_FOR_FS is not set\nCONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y\nCONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y\nCONFIG_USE_PERCPU_NUMA_NODE_ID=y\nCONFIG_HAVE_SETUP_PER_CPU_AREA=y\nCONFIG_FRONTSWAP=y\n# CONFIG_CMA is not set\nCONFIG_GENERIC_EARLY_IOREMAP=y\n# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set\n# CONFIG_IDLE_PAGE_TRACKING is not set\nCONFIG_ARCH_HAS_CACHE_LINE_SIZE=y\nCONFIG_ARCH_HAS_CURRENT_STACK_POINTER=y\nCONFIG_ARCH_HAS_PTE_DEVMAP=y\nCONFIG_ARCH_HAS_ZONE_DMA_SET=y\nCONFIG_ZONE_DMA=y\nCONFIG_ZONE_DMA32=y\nCONFIG_ZONE_DEVICE=y\n# CONFIG_DEVICE_PRIVATE is not set\nCONFIG_ARCH_USES_HIGH_VMA_FLAGS=y\nCONFIG_VM_EVENT_COUNTERS=y\nCONFIG_PERCPU_STATS=y\n# CONFIG_GUP_TEST is not set\nCONFIG_ARCH_HAS_PTE_SPECIAL=y\nCONFIG_SECRETMEM=y\n# CONFIG_ANON_VMA_NAME is not set\nCONFIG_USERFAULTFD=y\nCONFIG_HAVE_ARCH_USERFAULTFD_MINOR=y\n# CONFIG_LRU_GEN is not set\nCONFIG_LOCK_MM_AND_FIND_VMA=y\n\n#\n# Data Access Monitoring\n#\n# CONFIG_DAMON is not set\n# end of Data Access Monitoring\n# end of Memory Management options\n\nCONFIG_NET=y\nCONFIG_NET_INGRESS=y\nCONFIG_NET_EGRESS=y\nCONFIG_SKB_EXTENSIONS=y\n\n#\n# Networking options\n#\nCONFIG_PACKET=y\nCONFIG_PACKET_DIAG=y\nCONFIG_UNIX=y\nCONFIG_UNIX_SCM=y\nCONFIG_AF_UNIX_OOB=y\nCONFIG_UNIX_DIAG=y\nCONFIG_TLS=y\n# CONFIG_TLS_DEVICE is not set\n# CONFIG_TLS_TOE is not set\nCONFIG_XFRM=y\nCONFIG_XFRM_OFFLOAD=y\nCONFIG_XFRM_ALGO=y\nCONFIG_XFRM_USER=y\n# CONFIG_XFRM_INTERFACE is not set\nCONFIG_XFRM_SUB_POLICY=y\nCONFIG_XFRM_MIGRATE=y\nCONFIG_XFRM_STATISTICS=y\nCONFIG_XFRM_AH=y\nCONFIG_XFRM_ESP=y\nCONFIG_NET_KEY=y\nCONFIG_NET_KEY_MIGRATE=y\nCONFIG_XFRM_ESPINTCP=y\nCONFIG_XDP_SOCKETS=y\n# CONFIG_XDP_SOCKETS_DIAG is not set\nCONFIG_INET=y\n# CONFIG_IP_MULTICAST is not set\nCONFIG_IP_ADVANCED_ROUTER=y\n# CONFIG_IP_FIB_TRIE_STATS is not set\nCONFIG_IP_MULTIPLE_TABLES=y\n# CONFIG_IP_ROUTE_MULTIPATH is not set\n# CONFIG_IP_ROUTE_VERBOSE is not set\nCONFIG_IP_ROUTE_CLASSID=y\nCONFIG_IP_PNP=y\nCONFIG_IP_PNP_DHCP=y\nCONFIG_IP_PNP_BOOTP=y\nCONFIG_IP_PNP_RARP=y\nCONFIG_NET_IPIP=y\n# CONFIG_NET_IPGRE_DEMUX is not set\nCONFIG_NET_IP_TUNNEL=y\nCONFIG_SYN_COOKIES=y\n# CONFIG_NET_IPVTI is not set\nCONFIG_NET_UDP_TUNNEL=y\nCONFIG_NET_FOU=y\nCONFIG_NET_FOU_IP_TUNNELS=y\n# CONFIG_INET_AH is not set\n# CONFIG_INET_ESP is not set\n# CONFIG_INET_IPCOMP is not set\nCONFIG_INET_TABLE_PERTURB_ORDER=16\nCONFIG_INET_TUNNEL=y\nCONFIG_INET_DIAG=y\nCONFIG_INET_TCP_DIAG=y\nCONFIG_INET_UDP_DIAG=y\nCONFIG_INET_RAW_DIAG=y\n# CONFIG_INET_DIAG_DESTROY is not set\n# CONFIG_TCP_CONG_ADVANCED is not set\nCONFIG_TCP_CONG_CUBIC=y\nCONFIG_DEFAULT_TCP_CONG=\"cubic\"\n# CONFIG_TCP_MD5SIG is not set\nCONFIG_IPV6=y\nCONFIG_IPV6_ROUTER_PREF=y\nCONFIG_IPV6_ROUTE_INFO=y\nCONFIG_IPV6_OPTIMISTIC_DAD=y\nCONFIG_INET6_AH=y\nCONFIG_INET6_ESP=y\nCONFIG_INET6_ESP_OFFLOAD=y\nCONFIG_INET6_ESPINTCP=y\n# CONFIG_INET6_IPCOMP is not set\n# CONFIG_IPV6_MIP6 is not set\n# CONFIG_IPV6_ILA is not set\nCONFIG_INET6_TUNNEL=y\n# CONFIG_IPV6_VTI is not set\nCONFIG_IPV6_SIT=y\n# CONFIG_IPV6_SIT_6RD is not set\nCONFIG_IPV6_NDISC_NODETYPE=y\nCONFIG_IPV6_TUNNEL=y\nCONFIG_IPV6_FOU=y\nCONFIG_IPV6_FOU_TUNNEL=y\nCONFIG_IPV6_MULTIPLE_TABLES=y\n# CONFIG_IPV6_SUBTREES is not set\n# CONFIG_IPV6_MROUTE is not set\n# CONFIG_IPV6_SEG6_LWTUNNEL is not set\n# CONFIG_IPV6_SEG6_HMAC is not set\n# CONFIG_IPV6_RPL_LWTUNNEL is not set\n# CONFIG_IPV6_IOAM6_LWTUNNEL is not set\n# CONFIG_MPTCP is not set\nCONFIG_NETWORK_SECMARK=y\nCONFIG_NET_PTP_CLASSIFY=y\n# CONFIG_NETWORK_PHY_TIMESTAMPING is not set\nCONFIG_NETFILTER=y\nCONFIG_NETFILTER_ADVANCED=y\nCONFIG_BRIDGE_NETFILTER=y\n\n#\n# Core Netfilter Configuration\n#\nCONFIG_NETFILTER_INGRESS=y\nCONFIG_NETFILTER_EGRESS=y\nCONFIG_NETFILTER_SKIP_EGRESS=y\nCONFIG_NETFILTER_NETLINK=y\nCONFIG_NETFILTER_FAMILY_BRIDGE=y\nCONFIG_NETFILTER_FAMILY_ARP=y\n# CONFIG_NETFILTER_NETLINK_HOOK is not set\nCONFIG_NETFILTER_NETLINK_ACCT=y\nCONFIG_NETFILTER_NETLINK_QUEUE=y\nCONFIG_NETFILTER_NETLINK_LOG=y\nCONFIG_NETFILTER_NETLINK_OSF=y\nCONFIG_NF_CONNTRACK=y\nCONFIG_NF_LOG_SYSLOG=y\nCONFIG_NETFILTER_CONNCOUNT=y\nCONFIG_NF_CONNTRACK_MARK=y\n# CONFIG_NF_CONNTRACK_SECMARK is not set\nCONFIG_NF_CONNTRACK_ZONES=y\nCONFIG_NF_CONNTRACK_PROCFS=y\nCONFIG_NF_CONNTRACK_EVENTS=y\nCONFIG_NF_CONNTRACK_TIMEOUT=y\nCONFIG_NF_CONNTRACK_TIMESTAMP=y\nCONFIG_NF_CONNTRACK_LABELS=y\nCONFIG_NF_CT_PROTO_DCCP=y\nCONFIG_NF_CT_PROTO_SCTP=y\nCONFIG_NF_CT_PROTO_UDPLITE=y\n# CONFIG_NF_CONNTRACK_AMANDA is not set\n# CONFIG_NF_CONNTRACK_FTP is not set\n# CONFIG_NF_CONNTRACK_H323 is not set\n# CONFIG_NF_CONNTRACK_IRC is not set\n# CONFIG_NF_CONNTRACK_NETBIOS_NS is not set\n# CONFIG_NF_CONNTRACK_SNMP is not set\n# CONFIG_NF_CONNTRACK_PPTP is not set\n# CONFIG_NF_CONNTRACK_SANE is not set\n# CONFIG_NF_CONNTRACK_SIP is not set\n# CONFIG_NF_CONNTRACK_TFTP is not set\nCONFIG_NF_CT_NETLINK=y\n# CONFIG_NF_CT_NETLINK_TIMEOUT is not set\n# CONFIG_NETFILTER_NETLINK_GLUE_CT is not set\nCONFIG_NF_NAT=y\nCONFIG_NF_NAT_REDIRECT=y\nCONFIG_NF_NAT_MASQUERADE=y\nCONFIG_NETFILTER_SYNPROXY=y\nCONFIG_NF_TABLES=y\nCONFIG_NF_TABLES_INET=y\nCONFIG_NF_TABLES_NETDEV=y\nCONFIG_NFT_NUMGEN=y\nCONFIG_NFT_CT=y\nCONFIG_NFT_CONNLIMIT=y\nCONFIG_NFT_LOG=y\nCONFIG_NFT_LIMIT=y\nCONFIG_NFT_MASQ=y\nCONFIG_NFT_REDIR=y\nCONFIG_NFT_NAT=y\nCONFIG_NFT_TUNNEL=y\nCONFIG_NFT_OBJREF=y\nCONFIG_NFT_QUEUE=y\nCONFIG_NFT_QUOTA=y\nCONFIG_NFT_REJECT=y\nCONFIG_NFT_REJECT_INET=y\nCONFIG_NFT_COMPAT=y\nCONFIG_NFT_HASH=y\nCONFIG_NFT_FIB=y\nCONFIG_NFT_FIB_INET=y\nCONFIG_NFT_XFRM=y\nCONFIG_NFT_SOCKET=y\nCONFIG_NFT_OSF=y\nCONFIG_NFT_TPROXY=y\nCONFIG_NFT_SYNPROXY=y\nCONFIG_NF_DUP_NETDEV=y\nCONFIG_NFT_DUP_NETDEV=y\nCONFIG_NFT_FWD_NETDEV=y\nCONFIG_NFT_FIB_NETDEV=y\nCONFIG_NFT_REJECT_NETDEV=y\n# CONFIG_NF_FLOW_TABLE is not set\nCONFIG_NETFILTER_XTABLES=y\n\n#\n# Xtables combined modules\n#\nCONFIG_NETFILTER_XT_MARK=y\nCONFIG_NETFILTER_XT_CONNMARK=y\nCONFIG_NETFILTER_XT_SET=y\n\n#\n# Xtables targets\n#\nCONFIG_NETFILTER_XT_TARGET_AUDIT=y\nCONFIG_NETFILTER_XT_TARGET_CHECKSUM=y\nCONFIG_NETFILTER_XT_TARGET_CLASSIFY=y\nCONFIG_NETFILTER_XT_TARGET_CONNMARK=y\nCONFIG_NETFILTER_XT_TARGET_CT=y\nCONFIG_NETFILTER_XT_TARGET_DSCP=y\nCONFIG_NETFILTER_XT_TARGET_HL=y\nCONFIG_NETFILTER_XT_TARGET_HMARK=y\nCONFIG_NETFILTER_XT_TARGET_IDLETIMER=y\nCONFIG_NETFILTER_XT_TARGET_LOG=y\nCONFIG_NETFILTER_XT_TARGET_MARK=y\nCONFIG_NETFILTER_XT_NAT=y\nCONFIG_NETFILTER_XT_TARGET_NETMAP=y\nCONFIG_NETFILTER_XT_TARGET_NFLOG=y\nCONFIG_NETFILTER_XT_TARGET_NFQUEUE=y\nCONFIG_NETFILTER_XT_TARGET_NOTRACK=y\nCONFIG_NETFILTER_XT_TARGET_RATEEST=y\nCONFIG_NETFILTER_XT_TARGET_REDIRECT=y\nCONFIG_NETFILTER_XT_TARGET_MASQUERADE=y\nCONFIG_NETFILTER_XT_TARGET_TEE=y\nCONFIG_NETFILTER_XT_TARGET_TPROXY=y\nCONFIG_NETFILTER_XT_TARGET_TRACE=y\n# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set\nCONFIG_NETFILTER_XT_TARGET_TCPMSS=y\nCONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=y\n\n#\n# Xtables matches\n#\nCONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y\nCONFIG_NETFILTER_XT_MATCH_BPF=y\nCONFIG_NETFILTER_XT_MATCH_CGROUP=y\nCONFIG_NETFILTER_XT_MATCH_CLUSTER=y\nCONFIG_NETFILTER_XT_MATCH_COMMENT=y\nCONFIG_NETFILTER_XT_MATCH_CONNBYTES=y\nCONFIG_NETFILTER_XT_MATCH_CONNLABEL=y\nCONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y\nCONFIG_NETFILTER_XT_MATCH_CONNMARK=y\nCONFIG_NETFILTER_XT_MATCH_CONNTRACK=y\nCONFIG_NETFILTER_XT_MATCH_CPU=y\nCONFIG_NETFILTER_XT_MATCH_DCCP=y\nCONFIG_NETFILTER_XT_MATCH_DEVGROUP=y\nCONFIG_NETFILTER_XT_MATCH_DSCP=y\nCONFIG_NETFILTER_XT_MATCH_ECN=y\nCONFIG_NETFILTER_XT_MATCH_ESP=y\nCONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y\nCONFIG_NETFILTER_XT_MATCH_HELPER=y\nCONFIG_NETFILTER_XT_MATCH_HL=y\nCONFIG_NETFILTER_XT_MATCH_IPCOMP=y\nCONFIG_NETFILTER_XT_MATCH_IPRANGE=y\n# CONFIG_NETFILTER_XT_MATCH_IPVS is not set\nCONFIG_NETFILTER_XT_MATCH_L2TP=y\nCONFIG_NETFILTER_XT_MATCH_LENGTH=y\nCONFIG_NETFILTER_XT_MATCH_LIMIT=y\nCONFIG_NETFILTER_XT_MATCH_MAC=y\nCONFIG_NETFILTER_XT_MATCH_MARK=y\nCONFIG_NETFILTER_XT_MATCH_MULTIPORT=y\nCONFIG_NETFILTER_XT_MATCH_NFACCT=y\nCONFIG_NETFILTER_XT_MATCH_OSF=y\nCONFIG_NETFILTER_XT_MATCH_OWNER=y\nCONFIG_NETFILTER_XT_MATCH_POLICY=y\nCONFIG_NETFILTER_XT_MATCH_PHYSDEV=y\nCONFIG_NETFILTER_XT_MATCH_PKTTYPE=y\nCONFIG_NETFILTER_XT_MATCH_QUOTA=y\nCONFIG_NETFILTER_XT_MATCH_RATEEST=y\nCONFIG_NETFILTER_XT_MATCH_REALM=y\nCONFIG_NETFILTER_XT_MATCH_RECENT=y\nCONFIG_NETFILTER_XT_MATCH_SCTP=y\nCONFIG_NETFILTER_XT_MATCH_SOCKET=y\nCONFIG_NETFILTER_XT_MATCH_STATE=y\nCONFIG_NETFILTER_XT_MATCH_STATISTIC=y\nCONFIG_NETFILTER_XT_MATCH_STRING=y\nCONFIG_NETFILTER_XT_MATCH_TCPMSS=y\nCONFIG_NETFILTER_XT_MATCH_TIME=y\nCONFIG_NETFILTER_XT_MATCH_U32=y\n# end of Core Netfilter Configuration\n\nCONFIG_IP_SET=y\nCONFIG_IP_SET_MAX=256\n# CONFIG_IP_SET_BITMAP_IP is not set\n# CONFIG_IP_SET_BITMAP_IPMAC is not set\n# CONFIG_IP_SET_BITMAP_PORT is not set\nCONFIG_IP_SET_HASH_IP=y\n# CONFIG_IP_SET_HASH_IPMARK is not set\n# CONFIG_IP_SET_HASH_IPPORT is not set\n# CONFIG_IP_SET_HASH_IPPORTIP is not set\n# CONFIG_IP_SET_HASH_IPPORTNET is not set\n# CONFIG_IP_SET_HASH_IPMAC is not set\n# CONFIG_IP_SET_HASH_MAC is not set\n# CONFIG_IP_SET_HASH_NETPORTNET is not set\n# CONFIG_IP_SET_HASH_NET is not set\n# CONFIG_IP_SET_HASH_NETNET is not set\n# CONFIG_IP_SET_HASH_NETPORT is not set\n# CONFIG_IP_SET_HASH_NETIFACE is not set\n# CONFIG_IP_SET_LIST_SET is not set\nCONFIG_IP_VS=y\n# CONFIG_IP_VS_IPV6 is not set\n# CONFIG_IP_VS_DEBUG is not set\nCONFIG_IP_VS_TAB_BITS=12\n\n#\n# IPVS transport protocol load balancing support\n#\nCONFIG_IP_VS_PROTO_TCP=y\nCONFIG_IP_VS_PROTO_UDP=y\nCONFIG_IP_VS_PROTO_AH_ESP=y\nCONFIG_IP_VS_PROTO_ESP=y\nCONFIG_IP_VS_PROTO_AH=y\nCONFIG_IP_VS_PROTO_SCTP=y\n\n#\n# IPVS scheduler\n#\n# CONFIG_IP_VS_RR is not set\n# CONFIG_IP_VS_WRR is not set\n# CONFIG_IP_VS_LC is not set\n# CONFIG_IP_VS_WLC is not set\n# CONFIG_IP_VS_FO is not set\n# CONFIG_IP_VS_OVF is not set\n# CONFIG_IP_VS_LBLC is not set\n# CONFIG_IP_VS_LBLCR is not set\nCONFIG_IP_VS_DH=y\n# CONFIG_IP_VS_SH is not set\n# CONFIG_IP_VS_MH is not set\n# CONFIG_IP_VS_SED is not set\n# CONFIG_IP_VS_NQ is not set\n# CONFIG_IP_VS_TWOS is not set\n\n#\n# IPVS SH scheduler\n#\nCONFIG_IP_VS_SH_TAB_BITS=8\n\n#\n# IPVS MH scheduler\n#\nCONFIG_IP_VS_MH_TAB_INDEX=12\n\n#\n# IPVS application helper\n#\n# CONFIG_IP_VS_NFCT is not set\n\n#\n# IP: Netfilter Configuration\n#\nCONFIG_NF_DEFRAG_IPV4=y\nCONFIG_NF_SOCKET_IPV4=y\nCONFIG_NF_TPROXY_IPV4=y\nCONFIG_NF_TABLES_IPV4=y\nCONFIG_NFT_REJECT_IPV4=y\nCONFIG_NFT_DUP_IPV4=y\nCONFIG_NFT_FIB_IPV4=y\nCONFIG_NF_TABLES_ARP=y\nCONFIG_NF_DUP_IPV4=y\nCONFIG_NF_LOG_ARP=y\nCONFIG_NF_LOG_IPV4=y\nCONFIG_NF_REJECT_IPV4=y\nCONFIG_IP_NF_IPTABLES=y\nCONFIG_IP_NF_MATCH_AH=y\nCONFIG_IP_NF_MATCH_ECN=y\nCONFIG_IP_NF_MATCH_RPFILTER=y\nCONFIG_IP_NF_MATCH_TTL=y\nCONFIG_IP_NF_FILTER=y\nCONFIG_IP_NF_TARGET_REJECT=y\nCONFIG_IP_NF_TARGET_SYNPROXY=y\nCONFIG_IP_NF_NAT=y\nCONFIG_IP_NF_TARGET_MASQUERADE=y\nCONFIG_IP_NF_TARGET_NETMAP=y\nCONFIG_IP_NF_TARGET_REDIRECT=y\nCONFIG_IP_NF_MANGLE=y\nCONFIG_IP_NF_TARGET_CLUSTERIP=y\nCONFIG_IP_NF_TARGET_ECN=y\nCONFIG_IP_NF_TARGET_TTL=y\nCONFIG_IP_NF_RAW=y\nCONFIG_IP_NF_ARPTABLES=y\nCONFIG_IP_NF_ARPFILTER=y\nCONFIG_IP_NF_ARP_MANGLE=y\n# end of IP: Netfilter Configuration\n\n#\n# IPv6: Netfilter Configuration\n#\nCONFIG_NF_SOCKET_IPV6=y\nCONFIG_NF_TPROXY_IPV6=y\nCONFIG_NF_TABLES_IPV6=y\nCONFIG_NFT_REJECT_IPV6=y\nCONFIG_NFT_DUP_IPV6=y\nCONFIG_NFT_FIB_IPV6=y\nCONFIG_NF_DUP_IPV6=y\nCONFIG_NF_REJECT_IPV6=y\nCONFIG_NF_LOG_IPV6=y\nCONFIG_IP6_NF_IPTABLES=y\nCONFIG_IP6_NF_MATCH_AH=y\nCONFIG_IP6_NF_MATCH_EUI64=y\nCONFIG_IP6_NF_MATCH_FRAG=y\nCONFIG_IP6_NF_MATCH_OPTS=y\nCONFIG_IP6_NF_MATCH_HL=y\nCONFIG_IP6_NF_MATCH_IPV6HEADER=y\nCONFIG_IP6_NF_MATCH_MH=y\nCONFIG_IP6_NF_MATCH_RPFILTER=y\nCONFIG_IP6_NF_MATCH_RT=y\nCONFIG_IP6_NF_MATCH_SRH=y\nCONFIG_IP6_NF_TARGET_HL=y\nCONFIG_IP6_NF_FILTER=y\nCONFIG_IP6_NF_TARGET_REJECT=y\nCONFIG_IP6_NF_TARGET_SYNPROXY=y\nCONFIG_IP6_NF_MANGLE=y\nCONFIG_IP6_NF_RAW=y\nCONFIG_IP6_NF_NAT=y\nCONFIG_IP6_NF_TARGET_MASQUERADE=y\nCONFIG_IP6_NF_TARGET_NPT=y\n# end of IPv6: Netfilter Configuration\n\nCONFIG_NF_DEFRAG_IPV6=y\nCONFIG_NF_TABLES_BRIDGE=y\n# CONFIG_NFT_BRIDGE_META is not set\n# CONFIG_NFT_BRIDGE_REJECT is not set\nCONFIG_NF_CONNTRACK_BRIDGE=y\nCONFIG_BRIDGE_NF_EBTABLES=y\nCONFIG_BRIDGE_EBT_BROUTE=y\nCONFIG_BRIDGE_EBT_T_FILTER=y\nCONFIG_BRIDGE_EBT_T_NAT=y\nCONFIG_BRIDGE_EBT_802_3=y\nCONFIG_BRIDGE_EBT_AMONG=y\nCONFIG_BRIDGE_EBT_ARP=y\nCONFIG_BRIDGE_EBT_IP=y\nCONFIG_BRIDGE_EBT_IP6=y\nCONFIG_BRIDGE_EBT_LIMIT=y\nCONFIG_BRIDGE_EBT_MARK=y\nCONFIG_BRIDGE_EBT_PKTTYPE=y\nCONFIG_BRIDGE_EBT_STP=y\nCONFIG_BRIDGE_EBT_VLAN=y\nCONFIG_BRIDGE_EBT_ARPREPLY=y\nCONFIG_BRIDGE_EBT_DNAT=y\nCONFIG_BRIDGE_EBT_MARK_T=y\nCONFIG_BRIDGE_EBT_REDIRECT=y\nCONFIG_BRIDGE_EBT_SNAT=y\nCONFIG_BRIDGE_EBT_LOG=y\nCONFIG_BRIDGE_EBT_NFLOG=y\nCONFIG_BPFILTER=y\nCONFIG_BPFILTER_UMH=y\n# CONFIG_IP_DCCP is not set\nCONFIG_IP_SCTP=y\n# CONFIG_SCTP_DBG_OBJCNT is not set\nCONFIG_SCTP_DEFAULT_COOKIE_HMAC_MD5=y\n# CONFIG_SCTP_DEFAULT_COOKIE_HMAC_SHA1 is not set\n# CONFIG_SCTP_DEFAULT_COOKIE_HMAC_NONE is not set\nCONFIG_SCTP_COOKIE_HMAC_MD5=y\n# CONFIG_SCTP_COOKIE_HMAC_SHA1 is not set\nCONFIG_INET_SCTP_DIAG=y\n# CONFIG_RDS is not set\n# CONFIG_TIPC is not set\n# CONFIG_ATM is not set\n# CONFIG_L2TP is not set\nCONFIG_STP=y\nCONFIG_BRIDGE=y\nCONFIG_BRIDGE_IGMP_SNOOPING=y\n# CONFIG_BRIDGE_VLAN_FILTERING is not set\n# CONFIG_BRIDGE_MRP is not set\n# CONFIG_BRIDGE_CFM is not set\n# CONFIG_NET_DSA is not set\nCONFIG_VLAN_8021Q=y\n# CONFIG_VLAN_8021Q_GVRP is not set\n# CONFIG_VLAN_8021Q_MVRP is not set\nCONFIG_LLC=y\n# CONFIG_LLC2 is not set\n# CONFIG_ATALK is not set\n# CONFIG_X25 is not set\n# CONFIG_LAPB is not set\n# CONFIG_PHONET is not set\n# CONFIG_6LOWPAN is not set\n# CONFIG_IEEE802154 is not set\nCONFIG_NET_SCHED=y\n\n#\n# Queueing/Scheduling\n#\nCONFIG_NET_SCH_CBQ=y\nCONFIG_NET_SCH_HTB=y\nCONFIG_NET_SCH_HFSC=y\nCONFIG_NET_SCH_PRIO=y\nCONFIG_NET_SCH_MULTIQ=y\nCONFIG_NET_SCH_RED=y\nCONFIG_NET_SCH_SFB=y\nCONFIG_NET_SCH_SFQ=y\nCONFIG_NET_SCH_TEQL=y\nCONFIG_NET_SCH_TBF=y\nCONFIG_NET_SCH_CBS=y\nCONFIG_NET_SCH_ETF=y\nCONFIG_NET_SCH_TAPRIO=y\nCONFIG_NET_SCH_GRED=y\nCONFIG_NET_SCH_DSMARK=y\nCONFIG_NET_SCH_NETEM=y\nCONFIG_NET_SCH_DRR=y\nCONFIG_NET_SCH_MQPRIO=y\nCONFIG_NET_SCH_SKBPRIO=y\nCONFIG_NET_SCH_CHOKE=y\nCONFIG_NET_SCH_QFQ=y\nCONFIG_NET_SCH_CODEL=y\nCONFIG_NET_SCH_FQ_CODEL=y\nCONFIG_NET_SCH_CAKE=y\nCONFIG_NET_SCH_FQ=y\nCONFIG_NET_SCH_HHF=y\nCONFIG_NET_SCH_PIE=y\nCONFIG_NET_SCH_FQ_PIE=y\nCONFIG_NET_SCH_INGRESS=y\nCONFIG_NET_SCH_PLUG=y\nCONFIG_NET_SCH_ETS=y\n# CONFIG_NET_SCH_DEFAULT is not set\n\n#\n# Classification\n#\nCONFIG_NET_CLS=y\nCONFIG_NET_CLS_BASIC=y\nCONFIG_NET_CLS_ROUTE4=y\nCONFIG_NET_CLS_FW=y\nCONFIG_NET_CLS_U32=y\nCONFIG_CLS_U32_PERF=y\nCONFIG_CLS_U32_MARK=y\nCONFIG_NET_CLS_FLOW=y\nCONFIG_NET_CLS_CGROUP=y\nCONFIG_NET_CLS_BPF=y\nCONFIG_NET_CLS_FLOWER=y\nCONFIG_NET_CLS_MATCHALL=y\nCONFIG_NET_EMATCH=y\nCONFIG_NET_EMATCH_STACK=32\nCONFIG_NET_EMATCH_CMP=y\nCONFIG_NET_EMATCH_NBYTE=y\nCONFIG_NET_EMATCH_U32=y\nCONFIG_NET_EMATCH_META=y\nCONFIG_NET_EMATCH_TEXT=y\n# CONFIG_NET_EMATCH_IPSET is not set\nCONFIG_NET_EMATCH_IPT=y\nCONFIG_NET_CLS_ACT=y\nCONFIG_NET_ACT_POLICE=y\nCONFIG_NET_ACT_GACT=y\nCONFIG_GACT_PROB=y\nCONFIG_NET_ACT_MIRRED=y\nCONFIG_NET_ACT_SAMPLE=y\nCONFIG_NET_ACT_IPT=y\nCONFIG_NET_ACT_NAT=y\nCONFIG_NET_ACT_PEDIT=y\nCONFIG_NET_ACT_SIMP=y\nCONFIG_NET_ACT_SKBEDIT=y\nCONFIG_NET_ACT_CSUM=y\nCONFIG_NET_ACT_MPLS=y\nCONFIG_NET_ACT_VLAN=y\nCONFIG_NET_ACT_BPF=y\nCONFIG_NET_ACT_CONNMARK=y\nCONFIG_NET_ACT_CTINFO=y\nCONFIG_NET_ACT_SKBMOD=y\nCONFIG_NET_ACT_IFE=y\nCONFIG_NET_ACT_TUNNEL_KEY=y\nCONFIG_NET_ACT_GATE=y\nCONFIG_NET_IFE_SKBMARK=y\nCONFIG_NET_IFE_SKBPRIO=y\nCONFIG_NET_IFE_SKBTCINDEX=y\nCONFIG_NET_TC_SKB_EXT=y\nCONFIG_NET_SCH_FIFO=y\n# CONFIG_DCB is not set\nCONFIG_DNS_RESOLVER=y\n# CONFIG_BATMAN_ADV is not set\n# CONFIG_OPENVSWITCH is not set\nCONFIG_VSOCKETS=y\nCONFIG_VSOCKETS_DIAG=y\nCONFIG_VSOCKETS_LOOPBACK=y\nCONFIG_VIRTIO_VSOCKETS=y\nCONFIG_VIRTIO_VSOCKETS_COMMON=y\nCONFIG_NETLINK_DIAG=y\n# CONFIG_MPLS is not set\n# CONFIG_NET_NSH is not set\n# CONFIG_HSR is not set\n# CONFIG_NET_SWITCHDEV is not set\nCONFIG_NET_L3_MASTER_DEV=y\n# CONFIG_QRTR is not set\n# CONFIG_NET_NCSI is not set\nCONFIG_PCPU_DEV_REFCNT=y\nCONFIG_RPS=y\nCONFIG_RFS_ACCEL=y\nCONFIG_SOCK_RX_QUEUE_MAPPING=y\nCONFIG_XPS=y\nCONFIG_CGROUP_NET_PRIO=y\nCONFIG_CGROUP_NET_CLASSID=y\nCONFIG_NET_RX_BUSY_POLL=y\nCONFIG_BQL=y\n# CONFIG_BPF_STREAM_PARSER is not set\nCONFIG_NET_FLOW_LIMIT=y\n\n#\n# Network testing\n#\n# CONFIG_NET_PKTGEN is not set\n# end of Network testing\n# end of Networking options\n\n# CONFIG_HAMRADIO is not set\n# CONFIG_CAN is not set\n# CONFIG_BT is not set\n# CONFIG_AF_RXRPC is not set\n# CONFIG_AF_KCM is not set\nCONFIG_STREAM_PARSER=y\n# CONFIG_MCTP is not set\nCONFIG_FIB_RULES=y\n# CONFIG_WIRELESS is not set\n# CONFIG_RFKILL is not set\n# CONFIG_NET_9P is not set\n# CONFIG_CAIF is not set\n# CONFIG_CEPH_LIB is not set\n# CONFIG_NFC is not set\nCONFIG_PSAMPLE=y\nCONFIG_NET_IFE=y\n# CONFIG_LWTUNNEL is not set\nCONFIG_DST_CACHE=y\nCONFIG_GRO_CELLS=y\nCONFIG_NET_SOCK_MSG=y\nCONFIG_PAGE_POOL=y\n# CONFIG_PAGE_POOL_STATS is not set\nCONFIG_FAILOVER=y\n# CONFIG_ETHTOOL_NETLINK is not set\n\n#\n# Device Drivers\n#\nCONFIG_ARM_AMBA=y\nCONFIG_HAVE_PCI=y\nCONFIG_PCI=y\nCONFIG_PCI_DOMAINS=y\nCONFIG_PCI_DOMAINS_GENERIC=y\nCONFIG_PCI_SYSCALL=y\nCONFIG_PCIEPORTBUS=y\n# CONFIG_HOTPLUG_PCI_PCIE is not set\nCONFIG_PCIEAER=y\n# CONFIG_PCIEAER_INJECT is not set\n# CONFIG_PCIE_ECRC is not set\nCONFIG_PCIEASPM=y\nCONFIG_PCIEASPM_DEFAULT=y\n# CONFIG_PCIEASPM_POWERSAVE is not set\n# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set\n# CONFIG_PCIEASPM_PERFORMANCE is not set\nCONFIG_PCIE_PME=y\n# CONFIG_PCIE_DPC is not set\n# CONFIG_PCIE_PTM is not set\nCONFIG_PCI_MSI=y\nCONFIG_PCI_MSI_IRQ_DOMAIN=y\nCONFIG_PCI_QUIRKS=y\nCONFIG_PCI_DEBUG=y\nCONFIG_PCI_STUB=y\nCONFIG_PCI_ECAM=y\n# CONFIG_PCI_IOV is not set\n# CONFIG_PCI_PRI is not set\n# CONFIG_PCI_PASID is not set\n# CONFIG_PCI_P2PDMA is not set\nCONFIG_PCI_LABEL=y\n# CONFIG_PCIE_BUS_TUNE_OFF is not set\nCONFIG_PCIE_BUS_DEFAULT=y\n# CONFIG_PCIE_BUS_SAFE is not set\n# CONFIG_PCIE_BUS_PERFORMANCE is not set\n# CONFIG_PCIE_BUS_PEER2PEER is not set\nCONFIG_VGA_ARB=y\nCONFIG_VGA_ARB_MAX_GPUS=16\nCONFIG_HOTPLUG_PCI=y\nCONFIG_HOTPLUG_PCI_ACPI=y\n# CONFIG_HOTPLUG_PCI_ACPI_IBM is not set\n# CONFIG_HOTPLUG_PCI_CPCI is not set\n# CONFIG_HOTPLUG_PCI_SHPC is not set\n\n#\n# PCI controller drivers\n#\n# CONFIG_PCI_FTPCI100 is not set\nCONFIG_PCI_HOST_COMMON=y\nCONFIG_PCI_HOST_GENERIC=y\n# CONFIG_PCIE_XILINX is not set\n# CONFIG_PCI_XGENE is not set\n# CONFIG_PCIE_ALTERA is not set\n# CONFIG_PCI_HOST_THUNDER_PEM is not set\n# CONFIG_PCI_HOST_THUNDER_ECAM is not set\n# CONFIG_PCIE_MICROCHIP_HOST is not set\n\n#\n# DesignWare PCI Core Support\n#\n# CONFIG_PCIE_DW_PLAT_HOST is not set\n# CONFIG_PCI_HISI is not set\n# CONFIG_PCIE_KIRIN is not set\n# CONFIG_PCI_MESON is not set\n# CONFIG_PCIE_AL is not set\n# end of DesignWare PCI Core Support\n\n#\n# Mobiveil PCIe Core Support\n#\n# end of Mobiveil PCIe Core Support\n\n#\n# Cadence PCIe controllers support\n#\n# CONFIG_PCIE_CADENCE_PLAT_HOST is not set\n# CONFIG_PCI_J721E_HOST is not set\n# end of Cadence PCIe controllers support\n# end of PCI controller drivers\n\n#\n# PCI Endpoint\n#\n# CONFIG_PCI_ENDPOINT is not set\n# end of PCI Endpoint\n\n#\n# PCI switch controller drivers\n#\n# CONFIG_PCI_SW_SWITCHTEC is not set\n# end of PCI switch controller drivers\n\n# CONFIG_CXL_BUS is not set\n# CONFIG_PCCARD is not set\n# CONFIG_RAPIDIO is not set\n\n#\n# Generic Driver Options\n#\nCONFIG_UEVENT_HELPER=y\nCONFIG_UEVENT_HELPER_PATH=\"/sbin/hotplug\"\nCONFIG_DEVTMPFS=y\nCONFIG_DEVTMPFS_MOUNT=y\n# CONFIG_DEVTMPFS_SAFE is not set\nCONFIG_STANDALONE=y\nCONFIG_PREVENT_FIRMWARE_BUILD=y\n\n#\n# Firmware loader\n#\nCONFIG_FW_LOADER=y\nCONFIG_FW_LOADER_PAGED_BUF=y\nCONFIG_FW_LOADER_SYSFS=y\nCONFIG_EXTRA_FIRMWARE=\"\"\nCONFIG_FW_LOADER_USER_HELPER=y\n# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set\n# CONFIG_FW_LOADER_COMPRESS is not set\n# CONFIG_FW_CACHE is not set\n# CONFIG_FW_UPLOAD is not set\n# end of Firmware loader\n\nCONFIG_ALLOW_DEV_COREDUMP=y\n# CONFIG_DEBUG_DRIVER is not set\n# CONFIG_DEBUG_DEVRES is not set\n# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set\nCONFIG_GENERIC_CPU_AUTOPROBE=y\nCONFIG_GENERIC_CPU_VULNERABILITIES=y\nCONFIG_DMA_SHARED_BUFFER=y\n# CONFIG_DMA_FENCE_TRACE is not set\nCONFIG_GENERIC_ARCH_TOPOLOGY=y\nCONFIG_GENERIC_ARCH_NUMA=y\n# end of Generic Driver Options\n\n#\n# Bus devices\n#\n# CONFIG_BRCMSTB_GISB_ARB is not set\n# CONFIG_VEXPRESS_CONFIG is not set\n# CONFIG_MHI_BUS is not set\n# CONFIG_MHI_BUS_EP is not set\n# end of Bus devices\n\nCONFIG_CONNECTOR=y\nCONFIG_PROC_EVENTS=y\n\n#\n# Firmware Drivers\n#\n\n#\n# ARM System Control and Management Interface Protocol\n#\n# CONFIG_ARM_SCMI_PROTOCOL is not set\n# end of ARM System Control and Management Interface Protocol\n\n# CONFIG_ARM_SCPI_PROTOCOL is not set\nCONFIG_FIRMWARE_MEMMAP=y\nCONFIG_DMIID=y\nCONFIG_DMI_SYSFS=y\n# CONFIG_FW_CFG_SYSFS is not set\n# CONFIG_SYSFB_SIMPLEFB is not set\n# CONFIG_ARM_FFA_TRANSPORT is not set\n# CONFIG_GOOGLE_FIRMWARE is not set\n\n#\n# EFI (Extensible Firmware Interface) Support\n#\nCONFIG_EFI_ESRT=y\nCONFIG_EFI_PARAMS_FROM_FDT=y\nCONFIG_EFI_RUNTIME_WRAPPERS=y\nCONFIG_EFI_GENERIC_STUB=y\n# CONFIG_EFI_ZBOOT is not set\nCONFIG_EFI_ARMSTUB_DTB_LOADER=y\nCONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y\n# CONFIG_EFI_BOOTLOADER_CONTROL is not set\n# CONFIG_EFI_CAPSULE_LOADER is not set\n# CONFIG_EFI_TEST is not set\n# CONFIG_RESET_ATTACK_MITIGATION is not set\n# CONFIG_EFI_DISABLE_PCI_DMA is not set\nCONFIG_EFI_EARLYCON=y\n# CONFIG_EFI_CUSTOM_SSDT_OVERLAYS is not set\n# CONFIG_EFI_DISABLE_RUNTIME is not set\n# CONFIG_EFI_COCO_SECRET is not set\n# end of EFI (Extensible Firmware Interface) Support\n\nCONFIG_ARM_PSCI_FW=y\n# CONFIG_ARM_PSCI_CHECKER is not set\nCONFIG_HAVE_ARM_SMCCC=y\nCONFIG_HAVE_ARM_SMCCC_DISCOVERY=y\n# CONFIG_ARM_SMCCC_SOC_ID is not set\n\n#\n# Tegra firmware driver\n#\n# end of Tegra firmware driver\n# end of Firmware Drivers\n\n# CONFIG_GNSS is not set\n# CONFIG_MTD is not set\nCONFIG_DTC=y\nCONFIG_OF=y\n# CONFIG_OF_UNITTEST is not set\nCONFIG_OF_FLATTREE=y\nCONFIG_OF_EARLY_FLATTREE=y\nCONFIG_OF_KOBJ=y\nCONFIG_OF_ADDRESS=y\nCONFIG_OF_IRQ=y\nCONFIG_OF_RESERVED_MEM=y\n# CONFIG_OF_OVERLAY is not set\nCONFIG_OF_NUMA=y\n# CONFIG_PARPORT is not set\nCONFIG_PNP=y\n# CONFIG_PNP_DEBUG_MESSAGES is not set\n\n#\n# Protocols\n#\nCONFIG_PNPACPI=y\nCONFIG_BLK_DEV=y\nCONFIG_BLK_DEV_NULL_BLK=y\n# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set\nCONFIG_ZRAM=y\nCONFIG_ZRAM_DEF_COMP_LZORLE=y\n# CONFIG_ZRAM_DEF_COMP_LZO is not set\nCONFIG_ZRAM_DEF_COMP=\"lzo-rle\"\n# CONFIG_ZRAM_WRITEBACK is not set\n# CONFIG_ZRAM_MEMORY_TRACKING is not set\nCONFIG_BLK_DEV_LOOP=y\nCONFIG_BLK_DEV_LOOP_MIN_COUNT=8\n# CONFIG_BLK_DEV_DRBD is not set\n# CONFIG_BLK_DEV_NBD is not set\nCONFIG_BLK_DEV_RAM=y\nCONFIG_BLK_DEV_RAM_COUNT=16\nCONFIG_BLK_DEV_RAM_SIZE=16384\n# CONFIG_ATA_OVER_ETH is not set\nCONFIG_VIRTIO_BLK=y\n# CONFIG_BLK_DEV_RBD is not set\n# CONFIG_BLK_DEV_UBLK is not set\n\n#\n# NVME Support\n#\n# CONFIG_BLK_DEV_NVME is not set\n# CONFIG_NVME_FC is not set\n# CONFIG_NVME_TCP is not set\n# CONFIG_NVME_TARGET is not set\n# end of NVME Support\n\n#\n# Misc devices\n#\n# CONFIG_AD525X_DPOT is not set\n# CONFIG_DUMMY_IRQ is not set\n# CONFIG_PHANTOM is not set\n# CONFIG_TIFM_CORE is not set\n# CONFIG_ICS932S401 is not set\n# CONFIG_ENCLOSURE_SERVICES is not set\n# CONFIG_HP_ILO is not set\n# CONFIG_APDS9802ALS is not set\n# CONFIG_ISL29003 is not set\n# CONFIG_ISL29020 is not set\n# CONFIG_SENSORS_TSL2550 is not set\n# CONFIG_SENSORS_BH1770 is not set\n# CONFIG_SENSORS_APDS990X is not set\n# CONFIG_HMC6352 is not set\n# CONFIG_DS1682 is not set\n# CONFIG_SRAM is not set\n# CONFIG_DW_XDATA_PCIE is not set\n# CONFIG_PCI_ENDPOINT_TEST is not set\n# CONFIG_XILINX_SDFEC is not set\n# CONFIG_OPEN_DICE is not set\n# CONFIG_VCPU_STALL_DETECTOR is not set\n# CONFIG_C2PORT is not set\n\n#\n# EEPROM support\n#\n# CONFIG_EEPROM_AT24 is not set\n# CONFIG_EEPROM_LEGACY is not set\n# CONFIG_EEPROM_MAX6875 is not set\n# CONFIG_EEPROM_93CX6 is not set\n# CONFIG_EEPROM_IDT_89HPESX is not set\n# CONFIG_EEPROM_EE1004 is not set\n# end of EEPROM support\n\n# CONFIG_CB710_CORE is not set\n\n#\n# Texas Instruments shared transport line discipline\n#\n# CONFIG_TI_ST is not set\n# end of Texas Instruments shared transport line discipline\n\n# CONFIG_SENSORS_LIS3_I2C is not set\n# CONFIG_ALTERA_STAPL is not set\n# CONFIG_VMWARE_VMCI is not set\n# CONFIG_GENWQE is not set\n# CONFIG_ECHO is not set\n# CONFIG_BCM_VK is not set\n# CONFIG_MISC_ALCOR_PCI is not set\n# CONFIG_MISC_RTSX_PCI is not set\n# CONFIG_HABANA_AI is not set\n# CONFIG_UACCE is not set\n# CONFIG_PVPANIC is not set\n# CONFIG_GP_PCI1XXXX is not set\n# end of Misc devices\n\n#\n# SCSI device support\n#\nCONFIG_SCSI_MOD=y\n# CONFIG_RAID_ATTRS is not set\n# CONFIG_SCSI is not set\n# end of SCSI device support\n\n# CONFIG_ATA is not set\n# CONFIG_MD is not set\n# CONFIG_TARGET_CORE is not set\n# CONFIG_FUSION is not set\n\n#\n# IEEE 1394 (FireWire) support\n#\n# CONFIG_FIREWIRE is not set\n# CONFIG_FIREWIRE_NOSY is not set\n# end of IEEE 1394 (FireWire) support\n\nCONFIG_NETDEVICES=y\nCONFIG_NET_CORE=y\n# CONFIG_BONDING is not set\n# CONFIG_DUMMY is not set\nCONFIG_WIREGUARD=y\n# CONFIG_WIREGUARD_DEBUG is not set\n# CONFIG_EQUALIZER is not set\n# CONFIG_IFB is not set\n# CONFIG_NET_TEAM is not set\nCONFIG_MACVLAN=y\n# CONFIG_MACVTAP is not set\nCONFIG_IPVLAN_L3S=y\nCONFIG_IPVLAN=y\n# CONFIG_IPVTAP is not set\nCONFIG_VXLAN=y\nCONFIG_GENEVE=y\n# CONFIG_BAREUDP is not set\n# CONFIG_GTP is not set\n# CONFIG_MACSEC is not set\n# CONFIG_NETCONSOLE is not set\nCONFIG_TUN=y\n# CONFIG_TUN_VNET_CROSS_LE is not set\nCONFIG_VETH=y\nCONFIG_VIRTIO_NET=y\n# CONFIG_NLMON is not set\n# CONFIG_NET_VRF is not set\n# CONFIG_ARCNET is not set\n# CONFIG_ETHERNET is not set\n# CONFIG_FDDI is not set\n# CONFIG_HIPPI is not set\n# CONFIG_NET_SB1000 is not set\n# CONFIG_PHYLIB is not set\n# CONFIG_PSE_CONTROLLER is not set\n# CONFIG_MDIO_DEVICE is not set\n\n#\n# PCS device drivers\n#\n# end of PCS device drivers\n\n# CONFIG_PPP is not set\n# CONFIG_SLIP is not set\n\n#\n# Host-side USB support is needed for USB Network Adapter support\n#\n# CONFIG_WLAN is not set\n# CONFIG_WAN is not set\n\n#\n# Wireless WAN\n#\n# CONFIG_WWAN is not set\n# end of Wireless WAN\n\n# CONFIG_VMXNET3 is not set\n# CONFIG_FUJITSU_ES is not set\n# CONFIG_NETDEVSIM is not set\nCONFIG_NET_FAILOVER=y\n# CONFIG_ISDN is not set\n\n#\n# Input device support\n#\nCONFIG_INPUT=y\nCONFIG_INPUT_FF_MEMLESS=y\nCONFIG_INPUT_SPARSEKMAP=y\n# CONFIG_INPUT_MATRIXKMAP is not set\n\n#\n# Userland interfaces\n#\nCONFIG_INPUT_MOUSEDEV=y\nCONFIG_INPUT_MOUSEDEV_PSAUX=y\nCONFIG_INPUT_MOUSEDEV_SCREEN_X=1024\nCONFIG_INPUT_MOUSEDEV_SCREEN_Y=768\n# CONFIG_INPUT_JOYDEV is not set\nCONFIG_INPUT_EVDEV=y\n# CONFIG_INPUT_EVBUG is not set\n\n#\n# Input Device Drivers\n#\nCONFIG_INPUT_KEYBOARD=y\n# CONFIG_KEYBOARD_ADP5588 is not set\n# CONFIG_KEYBOARD_ADP5589 is not set\n# CONFIG_KEYBOARD_ATKBD is not set\n# CONFIG_KEYBOARD_QT1050 is not set\n# CONFIG_KEYBOARD_QT1070 is not set\n# CONFIG_KEYBOARD_QT2160 is not set\n# CONFIG_KEYBOARD_DLINK_DIR685 is not set\n# CONFIG_KEYBOARD_LKKBD is not set\nCONFIG_KEYBOARD_GPIO=y\nCONFIG_KEYBOARD_GPIO_POLLED=y\n# CONFIG_KEYBOARD_TCA6416 is not set\n# CONFIG_KEYBOARD_TCA8418 is not set\n# CONFIG_KEYBOARD_MATRIX is not set\n# CONFIG_KEYBOARD_LM8333 is not set\n# CONFIG_KEYBOARD_MAX7359 is not set\n# CONFIG_KEYBOARD_MCS is not set\n# CONFIG_KEYBOARD_MPR121 is not set\n# CONFIG_KEYBOARD_NEWTON is not set\n# CONFIG_KEYBOARD_OPENCORES is not set\n# CONFIG_KEYBOARD_SAMSUNG is not set\n# CONFIG_KEYBOARD_STOWAWAY is not set\n# CONFIG_KEYBOARD_SUNKBD is not set\n# CONFIG_KEYBOARD_OMAP4 is not set\n# CONFIG_KEYBOARD_XTKBD is not set\n# CONFIG_KEYBOARD_CAP11XX is not set\n# CONFIG_KEYBOARD_BCM is not set\n# CONFIG_KEYBOARD_CYPRESS_SF is not set\n# CONFIG_INPUT_MOUSE is not set\n# CONFIG_INPUT_JOYSTICK is not set\n# CONFIG_INPUT_TABLET is not set\n# CONFIG_INPUT_TOUCHSCREEN is not set\nCONFIG_INPUT_MISC=y\n# CONFIG_INPUT_AD714X is not set\n# CONFIG_INPUT_ATMEL_CAPTOUCH is not set\n# CONFIG_INPUT_BMA150 is not set\n# CONFIG_INPUT_E3X0_BUTTON is not set\n# CONFIG_INPUT_MMA8450 is not set\n# CONFIG_INPUT_GPIO_BEEPER is not set\n# CONFIG_INPUT_GPIO_DECODER is not set\n# CONFIG_INPUT_GPIO_VIBRA is not set\n# CONFIG_INPUT_KXTJ9 is not set\nCONFIG_INPUT_UINPUT=y\n# CONFIG_INPUT_PCF8574 is not set\n# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set\n# CONFIG_INPUT_DA7280_HAPTICS is not set\n# CONFIG_INPUT_ADXL34X is not set\n# CONFIG_INPUT_IQS269A is not set\n# CONFIG_INPUT_IQS626A is not set\n# CONFIG_INPUT_IQS7222 is not set\n# CONFIG_INPUT_CMA3000 is not set\n# CONFIG_INPUT_SOC_BUTTON_ARRAY is not set\n# CONFIG_INPUT_DRV260X_HAPTICS is not set\n# CONFIG_INPUT_DRV2665_HAPTICS is not set\n# CONFIG_INPUT_DRV2667_HAPTICS is not set\n# CONFIG_RMI4_CORE is not set\n\n#\n# Hardware I/O ports\n#\n# CONFIG_SERIO is not set\n# CONFIG_GAMEPORT is not set\n# end of Hardware I/O ports\n# end of Input device support\n\n#\n# Character devices\n#\nCONFIG_TTY=y\nCONFIG_VT=y\nCONFIG_CONSOLE_TRANSLATIONS=y\nCONFIG_VT_CONSOLE=y\nCONFIG_VT_CONSOLE_SLEEP=y\nCONFIG_HW_CONSOLE=y\nCONFIG_VT_HW_CONSOLE_BINDING=y\nCONFIG_UNIX98_PTYS=y\n# CONFIG_LEGACY_PTYS is not set\n# CONFIG_LDISC_AUTOLOAD is not set\n\n#\n# Serial drivers\n#\nCONFIG_SERIAL_EARLYCON=y\nCONFIG_SERIAL_8250=y\n# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set\nCONFIG_SERIAL_8250_PNP=y\n# CONFIG_SERIAL_8250_16550A_VARIANTS is not set\n# CONFIG_SERIAL_8250_FINTEK is not set\nCONFIG_SERIAL_8250_CONSOLE=y\nCONFIG_SERIAL_8250_DMA=y\nCONFIG_SERIAL_8250_PCI=y\nCONFIG_SERIAL_8250_EXAR=y\nCONFIG_SERIAL_8250_NR_UARTS=1\nCONFIG_SERIAL_8250_RUNTIME_UARTS=1\n# CONFIG_SERIAL_8250_EXTENDED is not set\nCONFIG_SERIAL_8250_FSL=y\n# CONFIG_SERIAL_8250_DW is not set\n# CONFIG_SERIAL_8250_RT288X is not set\nCONFIG_SERIAL_8250_PERICOM=y\nCONFIG_SERIAL_OF_PLATFORM=y\n\n#\n# Non-8250 serial port support\n#\n# CONFIG_SERIAL_AMBA_PL010 is not set\nCONFIG_SERIAL_AMBA_PL011=y\nCONFIG_SERIAL_AMBA_PL011_CONSOLE=y\n# CONFIG_SERIAL_EARLYCON_ARM_SEMIHOST is not set\n# CONFIG_SERIAL_UARTLITE is not set\nCONFIG_SERIAL_CORE=y\nCONFIG_SERIAL_CORE_CONSOLE=y\n# CONFIG_SERIAL_JSM is not set\n# CONFIG_SERIAL_SIFIVE is not set\n# CONFIG_SERIAL_SCCNXP is not set\n# CONFIG_SERIAL_SC16IS7XX is not set\n# CONFIG_SERIAL_ALTERA_JTAGUART is not set\n# CONFIG_SERIAL_ALTERA_UART is not set\n# CONFIG_SERIAL_XILINX_PS_UART is not set\nCONFIG_SERIAL_ARC=y\n# CONFIG_SERIAL_ARC_CONSOLE is not set\nCONFIG_SERIAL_ARC_NR_PORTS=1\n# CONFIG_SERIAL_RP2 is not set\n# CONFIG_SERIAL_FSL_LPUART is not set\n# CONFIG_SERIAL_FSL_LINFLEXUART is not set\n# CONFIG_SERIAL_CONEXANT_DIGICOLOR is not set\n# CONFIG_SERIAL_SPRD is not set\n# end of Serial drivers\n\nCONFIG_SERIAL_MCTRL_GPIO=y\n# CONFIG_SERIAL_NONSTANDARD is not set\n# CONFIG_N_GSM is not set\n# CONFIG_NOZOMI is not set\n# CONFIG_NULL_TTY is not set\nCONFIG_HVC_DRIVER=y\n# CONFIG_HVC_DCC is not set\nCONFIG_SERIAL_DEV_BUS=y\nCONFIG_SERIAL_DEV_CTRL_TTYPORT=y\n# CONFIG_TTY_PRINTK is not set\nCONFIG_VIRTIO_CONSOLE=y\n# CONFIG_IPMI_HANDLER is not set\nCONFIG_HW_RANDOM=y\n# CONFIG_HW_RANDOM_TIMERIOMEM is not set\n# CONFIG_HW_RANDOM_BA431 is not set\nCONFIG_HW_RANDOM_VIRTIO=y\n# CONFIG_HW_RANDOM_CCTRNG is not set\n# CONFIG_HW_RANDOM_XIPHERA is not set\nCONFIG_HW_RANDOM_ARM_SMCCC_TRNG=y\nCONFIG_HW_RANDOM_CN10K=y\n# CONFIG_APPLICOM is not set\nCONFIG_DEVMEM=y\nCONFIG_DEVPORT=y\n# CONFIG_TCG_TPM is not set\n# CONFIG_XILLYBUS is not set\n# CONFIG_RANDOM_TRUST_CPU is not set\n# CONFIG_RANDOM_TRUST_BOOTLOADER is not set\n# end of Character devices\n\n#\n# I2C support\n#\nCONFIG_I2C=y\nCONFIG_ACPI_I2C_OPREGION=y\nCONFIG_I2C_BOARDINFO=y\nCONFIG_I2C_COMPAT=y\n# CONFIG_I2C_CHARDEV is not set\n# CONFIG_I2C_MUX is not set\nCONFIG_I2C_HELPER_AUTO=y\nCONFIG_I2C_ALGOBIT=y\n\n#\n# I2C Hardware Bus support\n#\n\n#\n# PC SMBus host controller drivers\n#\n# CONFIG_I2C_ALI1535 is not set\n# CONFIG_I2C_ALI1563 is not set\n# CONFIG_I2C_ALI15X3 is not set\n# CONFIG_I2C_AMD756 is not set\n# CONFIG_I2C_AMD8111 is not set\n# CONFIG_I2C_AMD_MP2 is not set\n# CONFIG_I2C_I801 is not set\n# CONFIG_I2C_ISCH is not set\n# CONFIG_I2C_PIIX4 is not set\n# CONFIG_I2C_NFORCE2 is not set\n# CONFIG_I2C_NVIDIA_GPU is not set\n# CONFIG_I2C_SIS5595 is not set\n# CONFIG_I2C_SIS630 is not set\n# CONFIG_I2C_SIS96X is not set\n# CONFIG_I2C_VIA is not set\n# CONFIG_I2C_VIAPRO is not set\n\n#\n# ACPI drivers\n#\n# CONFIG_I2C_SCMI is not set\n\n#\n# I2C system bus drivers (mostly embedded / system-on-chip)\n#\n# CONFIG_I2C_CADENCE is not set\n# CONFIG_I2C_CBUS_GPIO is not set\n# CONFIG_I2C_DESIGNWARE_PLATFORM is not set\n# CONFIG_I2C_DESIGNWARE_PCI is not set\n# CONFIG_I2C_EMEV2 is not set\n# CONFIG_I2C_GPIO is not set\n# CONFIG_I2C_HISI is not set\n# CONFIG_I2C_NOMADIK is not set\n# CONFIG_I2C_OCORES is not set\n# CONFIG_I2C_PCA_PLATFORM is not set\n# CONFIG_I2C_RK3X is not set\n# CONFIG_I2C_SIMTEC is not set\n# CONFIG_I2C_THUNDERX is not set\n# CONFIG_I2C_XILINX is not set\n\n#\n# External I2C/SMBus adapter drivers\n#\n# CONFIG_I2C_PCI1XXXX is not set\n# CONFIG_I2C_TAOS_EVM is not set\n\n#\n# Other I2C/SMBus bus drivers\n#\n# CONFIG_I2C_VIRTIO is not set\n# end of I2C Hardware Bus support\n\n# CONFIG_I2C_SLAVE is not set\n# CONFIG_I2C_DEBUG_CORE is not set\n# CONFIG_I2C_DEBUG_ALGO is not set\n# CONFIG_I2C_DEBUG_BUS is not set\n# end of I2C support\n\n# CONFIG_I3C is not set\n# CONFIG_SPI is not set\n# CONFIG_SPMI is not set\n# CONFIG_HSI is not set\nCONFIG_PPS=y\nCONFIG_PPS_DEBUG=y\n\n#\n# PPS clients support\n#\nCONFIG_PPS_CLIENT_KTIMER=y\nCONFIG_PPS_CLIENT_LDISC=y\nCONFIG_PPS_CLIENT_GPIO=y\n\n#\n# PPS generators support\n#\n\n#\n# PTP clock support\n#\nCONFIG_PTP_1588_CLOCK=y\nCONFIG_PTP_1588_CLOCK_OPTIONAL=y\n\n#\n# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks.\n#\nCONFIG_PTP_1588_CLOCK_KVM=y\n# CONFIG_PTP_1588_CLOCK_IDT82P33 is not set\n# CONFIG_PTP_1588_CLOCK_IDTCM is not set\n# end of PTP clock support\n\n# CONFIG_PINCTRL is not set\nCONFIG_GPIOLIB=y\nCONFIG_GPIOLIB_FASTPATH_LIMIT=512\nCONFIG_OF_GPIO=y\nCONFIG_GPIO_ACPI=y\nCONFIG_GPIOLIB_IRQCHIP=y\n# CONFIG_DEBUG_GPIO is not set\n# CONFIG_GPIO_SYSFS is not set\n# CONFIG_GPIO_CDEV is not set\n\n#\n# Memory mapped GPIO drivers\n#\n# CONFIG_GPIO_74XX_MMIO is not set\n# CONFIG_GPIO_ALTERA is not set\n# CONFIG_GPIO_AMDPT is not set\n# CONFIG_GPIO_CADENCE is not set\n# CONFIG_GPIO_DWAPB is not set\n# CONFIG_GPIO_EXAR is not set\n# CONFIG_GPIO_FTGPIO010 is not set\n# CONFIG_GPIO_GENERIC_PLATFORM is not set\n# CONFIG_GPIO_GRGPIO is not set\n# CONFIG_GPIO_HISI is not set\n# CONFIG_GPIO_HLWD is not set\n# CONFIG_GPIO_MB86S7X is not set\nCONFIG_GPIO_PL061=y\n# CONFIG_GPIO_SIFIVE is not set\n# CONFIG_GPIO_XGENE is not set\n# CONFIG_GPIO_XILINX is not set\n# CONFIG_GPIO_AMD_FCH is not set\n# end of Memory mapped GPIO drivers\n\n#\n# I2C GPIO expanders\n#\n# CONFIG_GPIO_ADNP is not set\n# CONFIG_GPIO_GW_PLD is not set\n# CONFIG_GPIO_MAX7300 is not set\n# CONFIG_GPIO_MAX732X is not set\n# CONFIG_GPIO_PCA953X is not set\n# CONFIG_GPIO_PCA9570 is not set\n# CONFIG_GPIO_PCF857X is not set\n# CONFIG_GPIO_TPIC2810 is not set\n# end of I2C GPIO expanders\n\n#\n# MFD GPIO expanders\n#\n# end of MFD GPIO expanders\n\n#\n# PCI GPIO expanders\n#\n# CONFIG_GPIO_BT8XX is not set\n# CONFIG_GPIO_PCI_IDIO_16 is not set\n# CONFIG_GPIO_PCIE_IDIO_24 is not set\n# CONFIG_GPIO_RDC321X is not set\n# end of PCI GPIO expanders\n\n#\n# Virtual GPIO drivers\n#\n# CONFIG_GPIO_AGGREGATOR is not set\n# CONFIG_GPIO_MOCKUP is not set\nCONFIG_GPIO_VIRTIO=y\n# CONFIG_GPIO_SIM is not set\n# end of Virtual GPIO drivers\n\n# CONFIG_W1 is not set\nCONFIG_POWER_RESET=y\n# CONFIG_POWER_RESET_GPIO is not set\n# CONFIG_POWER_RESET_GPIO_RESTART is not set\n# CONFIG_POWER_RESET_LTC2952 is not set\n# CONFIG_POWER_RESET_RESTART is not set\n# CONFIG_POWER_RESET_XGENE is not set\n# CONFIG_POWER_RESET_SYSCON is not set\n# CONFIG_POWER_RESET_SYSCON_POWEROFF is not set\n# CONFIG_NVMEM_REBOOT_MODE is not set\nCONFIG_POWER_SUPPLY=y\n# CONFIG_POWER_SUPPLY_DEBUG is not set\n# CONFIG_PDA_POWER is not set\n# CONFIG_IP5XXX_POWER is not set\n# CONFIG_TEST_POWER is not set\n# CONFIG_CHARGER_ADP5061 is not set\n# CONFIG_BATTERY_CW2015 is not set\n# CONFIG_BATTERY_DS2780 is not set\n# CONFIG_BATTERY_DS2781 is not set\n# CONFIG_BATTERY_DS2782 is not set\n# CONFIG_BATTERY_SAMSUNG_SDI is not set\n# CONFIG_BATTERY_SBS is not set\n# CONFIG_CHARGER_SBS is not set\n# CONFIG_BATTERY_BQ27XXX is not set\n# CONFIG_BATTERY_MAX17040 is not set\n# CONFIG_BATTERY_MAX17042 is not set\n# CONFIG_CHARGER_MAX8903 is not set\n# CONFIG_CHARGER_LP8727 is not set\n# CONFIG_CHARGER_GPIO is not set\n# CONFIG_CHARGER_LT3651 is not set\n# CONFIG_CHARGER_LTC4162L is not set\n# CONFIG_CHARGER_DETECTOR_MAX14656 is not set\n# CONFIG_CHARGER_MAX77976 is not set\n# CONFIG_CHARGER_BQ2415X is not set\n# CONFIG_CHARGER_BQ24257 is not set\n# CONFIG_CHARGER_BQ24735 is not set\n# CONFIG_CHARGER_BQ2515X is not set\n# CONFIG_CHARGER_BQ25890 is not set\n# CONFIG_CHARGER_BQ25980 is not set\n# CONFIG_CHARGER_BQ256XX is not set\n# CONFIG_BATTERY_GAUGE_LTC2941 is not set\n# CONFIG_BATTERY_GOLDFISH is not set\n# CONFIG_BATTERY_RT5033 is not set\n# CONFIG_CHARGER_RT9455 is not set\n# CONFIG_CHARGER_BD99954 is not set\n# CONFIG_BATTERY_UG3105 is not set\n# CONFIG_HWMON is not set\nCONFIG_THERMAL=y\n# CONFIG_THERMAL_NETLINK is not set\n# CONFIG_THERMAL_STATISTICS is not set\nCONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0\nCONFIG_THERMAL_OF=y\nCONFIG_THERMAL_WRITABLE_TRIPS=y\nCONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y\n# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set\n# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set\nCONFIG_THERMAL_GOV_FAIR_SHARE=y\nCONFIG_THERMAL_GOV_STEP_WISE=y\n# CONFIG_THERMAL_GOV_BANG_BANG is not set\nCONFIG_THERMAL_GOV_USER_SPACE=y\n# CONFIG_CPU_THERMAL is not set\n# CONFIG_THERMAL_EMULATION is not set\n# CONFIG_THERMAL_MMIO is not set\nCONFIG_WATCHDOG=y\nCONFIG_WATCHDOG_CORE=y\n# CONFIG_WATCHDOG_NOWAYOUT is not set\nCONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y\nCONFIG_WATCHDOG_OPEN_TIMEOUT=0\n# CONFIG_WATCHDOG_SYSFS is not set\n# CONFIG_WATCHDOG_HRTIMER_PRETIMEOUT is not set\n\n#\n# Watchdog Pretimeout Governors\n#\n# CONFIG_WATCHDOG_PRETIMEOUT_GOV is not set\n\n#\n# Watchdog Device Drivers\n#\n# CONFIG_SOFT_WATCHDOG is not set\n# CONFIG_GPIO_WATCHDOG is not set\n# CONFIG_WDAT_WDT is not set\n# CONFIG_XILINX_WATCHDOG is not set\n# CONFIG_ZIIRAVE_WATCHDOG is not set\n# CONFIG_ARM_SP805_WATCHDOG is not set\n# CONFIG_ARM_SBSA_WATCHDOG is not set\n# CONFIG_CADENCE_WATCHDOG is not set\n# CONFIG_DW_WATCHDOG is not set\n# CONFIG_MAX63XX_WATCHDOG is not set\n# CONFIG_ARM_SMC_WATCHDOG is not set\n# CONFIG_ALIM7101_WDT is not set\n# CONFIG_I6300ESB_WDT is not set\n# CONFIG_HP_WATCHDOG is not set\n# CONFIG_MEN_A21_WDT is not set\n\n#\n# PCI-based Watchdog Cards\n#\n# CONFIG_PCIPCWATCHDOG is not set\n# CONFIG_WDTPCI is not set\nCONFIG_SSB_POSSIBLE=y\n# CONFIG_SSB is not set\nCONFIG_BCMA_POSSIBLE=y\n# CONFIG_BCMA is not set\n\n#\n# Multifunction device drivers\n#\n# CONFIG_MFD_ACT8945A is not set\n# CONFIG_MFD_AS3711 is not set\n# CONFIG_MFD_AS3722 is not set\n# CONFIG_PMIC_ADP5520 is not set\n# CONFIG_MFD_AAT2870_CORE is not set\n# CONFIG_MFD_ATMEL_FLEXCOM is not set\n# CONFIG_MFD_ATMEL_HLCDC is not set\n# CONFIG_MFD_BCM590XX is not set\n# CONFIG_MFD_BD9571MWV is not set\n# CONFIG_MFD_AXP20X_I2C is not set\n# CONFIG_MFD_MADERA is not set\n# CONFIG_PMIC_DA903X is not set\n# CONFIG_MFD_DA9052_I2C is not set\n# CONFIG_MFD_DA9055 is not set\n# CONFIG_MFD_DA9062 is not set\n# CONFIG_MFD_DA9063 is not set\n# CONFIG_MFD_DA9150 is not set\n# CONFIG_MFD_GATEWORKS_GSC is not set\n# CONFIG_MFD_MC13XXX_I2C is not set\n# CONFIG_MFD_MP2629 is not set\n# CONFIG_MFD_HI6421_PMIC is not set\n# CONFIG_HTC_PASIC3 is not set\n# CONFIG_HTC_I2CPLD is not set\n# CONFIG_LPC_ICH is not set\n# CONFIG_LPC_SCH is not set\n# CONFIG_MFD_IQS62X is not set\n# CONFIG_MFD_JANZ_CMODIO is not set\n# CONFIG_MFD_KEMPLD is not set\n# CONFIG_MFD_88PM800 is not set\n# CONFIG_MFD_88PM805 is not set\n# CONFIG_MFD_88PM860X is not set\n# CONFIG_MFD_MAX14577 is not set\n# CONFIG_MFD_MAX77620 is not set\n# CONFIG_MFD_MAX77650 is not set\n# CONFIG_MFD_MAX77686 is not set\n# CONFIG_MFD_MAX77693 is not set\n# CONFIG_MFD_MAX77714 is not set\n# CONFIG_MFD_MAX77843 is not set\n# CONFIG_MFD_MAX8907 is not set\n# CONFIG_MFD_MAX8925 is not set\n# CONFIG_MFD_MAX8997 is not set\n# CONFIG_MFD_MAX8998 is not set\n# CONFIG_MFD_MT6360 is not set\n# CONFIG_MFD_MT6370 is not set\n# CONFIG_MFD_MT6397 is not set\n# CONFIG_MFD_MENF21BMC is not set\n# CONFIG_MFD_NTXEC is not set\n# CONFIG_MFD_RETU is not set\n# CONFIG_MFD_PCF50633 is not set\n# CONFIG_MFD_SY7636A is not set\n# CONFIG_MFD_RDC321X is not set\n# CONFIG_MFD_RT4831 is not set\n# CONFIG_MFD_RT5033 is not set\n# CONFIG_MFD_RT5120 is not set\n# CONFIG_MFD_RC5T583 is not set\n# CONFIG_MFD_RK808 is not set\n# CONFIG_MFD_RN5T618 is not set\n# CONFIG_MFD_SEC_CORE is not set\n# CONFIG_MFD_SI476X_CORE is not set\n# CONFIG_MFD_SM501 is not set\n# CONFIG_MFD_SKY81452 is not set\n# CONFIG_MFD_STMPE is not set\n# CONFIG_MFD_SYSCON is not set\n# CONFIG_MFD_TI_AM335X_TSCADC is not set\n# CONFIG_MFD_LP3943 is not set\n# CONFIG_MFD_LP8788 is not set\n# CONFIG_MFD_TI_LMU is not set\n# CONFIG_MFD_PALMAS is not set\n# CONFIG_TPS6105X is not set\n# CONFIG_TPS65010 is not set\n# CONFIG_TPS6507X is not set\n# CONFIG_MFD_TPS65086 is not set\n# CONFIG_MFD_TPS65090 is not set\n# CONFIG_MFD_TPS65217 is not set\n# CONFIG_MFD_TI_LP873X is not set\n# CONFIG_MFD_TI_LP87565 is not set\n# CONFIG_MFD_TPS65218 is not set\n# CONFIG_MFD_TPS6586X is not set\n# CONFIG_MFD_TPS65910 is not set\n# CONFIG_MFD_TPS65912_I2C is not set\n# CONFIG_TWL4030_CORE is not set\n# CONFIG_TWL6040_CORE is not set\n# CONFIG_MFD_WL1273_CORE is not set\n# CONFIG_MFD_LM3533 is not set\n# CONFIG_MFD_TC3589X is not set\n# CONFIG_MFD_TQMX86 is not set\n# CONFIG_MFD_VX855 is not set\n# CONFIG_MFD_LOCHNAGAR is not set\n# CONFIG_MFD_ARIZONA_I2C is not set\n# CONFIG_MFD_WM8400 is not set\n# CONFIG_MFD_WM831X_I2C is not set\n# CONFIG_MFD_WM8350_I2C is not set\n# CONFIG_MFD_WM8994 is not set\n# CONFIG_MFD_ROHM_BD718XX is not set\n# CONFIG_MFD_ROHM_BD71828 is not set\n# CONFIG_MFD_ROHM_BD957XMUF is not set\n# CONFIG_MFD_STPMIC1 is not set\n# CONFIG_MFD_STMFX is not set\n# CONFIG_MFD_ATC260X_I2C is not set\n# CONFIG_MFD_QCOM_PM8008 is not set\n# CONFIG_RAVE_SP_CORE is not set\n# CONFIG_MFD_RSMU_I2C is not set\n# end of Multifunction device drivers\n\n# CONFIG_REGULATOR is not set\n# CONFIG_RC_CORE is not set\n\n#\n# CEC support\n#\n# CONFIG_MEDIA_CEC_SUPPORT is not set\n# end of CEC support\n\n# CONFIG_MEDIA_SUPPORT is not set\n\n#\n# Graphics support\n#\nCONFIG_DRM=y\n# CONFIG_DRM_DEBUG_MM is not set\nCONFIG_DRM_KMS_HELPER=y\n# CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set\n# CONFIG_DRM_DEBUG_MODESET_LOCK is not set\nCONFIG_DRM_FBDEV_EMULATION=y\nCONFIG_DRM_FBDEV_OVERALLOC=100\n# CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set\n# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set\nCONFIG_DRM_GEM_SHMEM_HELPER=y\n\n#\n# I2C encoder or helper chips\n#\n# CONFIG_DRM_I2C_CH7006 is not set\n# CONFIG_DRM_I2C_SIL164 is not set\n# CONFIG_DRM_I2C_NXP_TDA998X is not set\n# CONFIG_DRM_I2C_NXP_TDA9950 is not set\n# end of I2C encoder or helper chips\n\n#\n# ARM devices\n#\n# CONFIG_DRM_HDLCD is not set\n# CONFIG_DRM_MALI_DISPLAY is not set\n# CONFIG_DRM_KOMEDA is not set\n# end of ARM devices\n\n# CONFIG_DRM_RADEON is not set\n# CONFIG_DRM_AMDGPU is not set\n# CONFIG_DRM_NOUVEAU is not set\n# CONFIG_DRM_VGEM is not set\n# CONFIG_DRM_VKMS is not set\n# CONFIG_DRM_VMWGFX is not set\n# CONFIG_DRM_AST is not set\n# CONFIG_DRM_MGAG200 is not set\n# CONFIG_DRM_RCAR_DW_HDMI is not set\n# CONFIG_DRM_RCAR_USE_LVDS is not set\n# CONFIG_DRM_RCAR_USE_MIPI_DSI is not set\n# CONFIG_DRM_QXL is not set\n# CONFIG_DRM_VIRTIO_GPU is not set\nCONFIG_DRM_PANEL=y\n\n#\n# Display Panels\n#\n# CONFIG_DRM_PANEL_LVDS is not set\n# CONFIG_DRM_PANEL_SIMPLE is not set\n# CONFIG_DRM_PANEL_EDP is not set\n# CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set\n# CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20 is not set\n# CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set\n# CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 is not set\n# CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0 is not set\n# CONFIG_DRM_PANEL_SEIKO_43WVF1G is not set\n# end of Display Panels\n\nCONFIG_DRM_BRIDGE=y\nCONFIG_DRM_PANEL_BRIDGE=y\n\n#\n# Display Interface Bridges\n#\n# CONFIG_DRM_CDNS_DSI is not set\n# CONFIG_DRM_CHIPONE_ICN6211 is not set\n# CONFIG_DRM_CHRONTEL_CH7033 is not set\n# CONFIG_DRM_DISPLAY_CONNECTOR is not set\n# CONFIG_DRM_ITE_IT6505 is not set\n# CONFIG_DRM_LONTIUM_LT8912B is not set\n# CONFIG_DRM_LONTIUM_LT9211 is not set\n# CONFIG_DRM_LONTIUM_LT9611 is not set\n# CONFIG_DRM_LONTIUM_LT9611UXC is not set\n# CONFIG_DRM_ITE_IT66121 is not set\n# CONFIG_DRM_LVDS_CODEC is not set\n# CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW is not set\n# CONFIG_DRM_NWL_MIPI_DSI is not set\n# CONFIG_DRM_NXP_PTN3460 is not set\n# CONFIG_DRM_PARADE_PS8622 is not set\n# CONFIG_DRM_PARADE_PS8640 is not set\n# CONFIG_DRM_SIL_SII8620 is not set\n# CONFIG_DRM_SII902X is not set\n# CONFIG_DRM_SII9234 is not set\n# CONFIG_DRM_SIMPLE_BRIDGE is not set\n# CONFIG_DRM_THINE_THC63LVD1024 is not set\n# CONFIG_DRM_TOSHIBA_TC358762 is not set\n# CONFIG_DRM_TOSHIBA_TC358764 is not set\n# CONFIG_DRM_TOSHIBA_TC358767 is not set\n# CONFIG_DRM_TOSHIBA_TC358768 is not set\n# CONFIG_DRM_TOSHIBA_TC358775 is not set\n# CONFIG_DRM_TI_DLPC3433 is not set\n# CONFIG_DRM_TI_TFP410 is not set\n# CONFIG_DRM_TI_SN65DSI83 is not set\n# CONFIG_DRM_TI_SN65DSI86 is not set\n# CONFIG_DRM_TI_TPD12S015 is not set\n# CONFIG_DRM_ANALOGIX_ANX6345 is not set\n# CONFIG_DRM_ANALOGIX_ANX78XX is not set\n# CONFIG_DRM_ANALOGIX_ANX7625 is not set\n# CONFIG_DRM_I2C_ADV7511 is not set\n# CONFIG_DRM_CDNS_MHDP8546 is not set\n# end of Display Interface Bridges\n\n# CONFIG_DRM_ETNAVIV is not set\n# CONFIG_DRM_HISI_HIBMC is not set\n# CONFIG_DRM_HISI_KIRIN is not set\n# CONFIG_DRM_LOGICVC is not set\n# CONFIG_DRM_ARCPGU is not set\n# CONFIG_DRM_BOCHS is not set\n# CONFIG_DRM_CIRRUS_QEMU is not set\n# CONFIG_DRM_SIMPLEDRM is not set\n# CONFIG_DRM_PL111 is not set\n# CONFIG_DRM_LIMA is not set\n# CONFIG_DRM_PANFROST is not set\n# CONFIG_DRM_TIDSS is not set\n# CONFIG_DRM_SSD130X is not set\n# CONFIG_DRM_LEGACY is not set\nCONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y\nCONFIG_DRM_NOMODESET=y\n\n#\n# Frame buffer Devices\n#\nCONFIG_FB_CMDLINE=y\nCONFIG_FB_NOTIFY=y\nCONFIG_FB=y\n# CONFIG_FIRMWARE_EDID is not set\nCONFIG_FB_CFB_FILLRECT=y\nCONFIG_FB_CFB_COPYAREA=y\nCONFIG_FB_CFB_IMAGEBLIT=y\nCONFIG_FB_SYS_FILLRECT=y\nCONFIG_FB_SYS_COPYAREA=y\nCONFIG_FB_SYS_IMAGEBLIT=y\n# CONFIG_FB_FOREIGN_ENDIAN is not set\nCONFIG_FB_SYS_FOPS=y\nCONFIG_FB_DEFERRED_IO=y\n# CONFIG_FB_MODE_HELPERS is not set\n# CONFIG_FB_TILEBLITTING is not set\n\n#\n# Frame buffer hardware drivers\n#\n# CONFIG_FB_CIRRUS is not set\n# CONFIG_FB_PM2 is not set\n# CONFIG_FB_ARMCLCD is not set\n# CONFIG_FB_CYBER2000 is not set\n# CONFIG_FB_ASILIANT is not set\n# CONFIG_FB_IMSTT is not set\n# CONFIG_FB_UVESA is not set\n# CONFIG_FB_EFI is not set\n# CONFIG_FB_OPENCORES is not set\n# CONFIG_FB_S1D13XXX is not set\n# CONFIG_FB_NVIDIA is not set\n# CONFIG_FB_RIVA is not set\n# CONFIG_FB_I740 is not set\n# CONFIG_FB_MATROX is not set\n# CONFIG_FB_RADEON is not set\n# CONFIG_FB_ATY128 is not set\n# CONFIG_FB_ATY is not set\n# CONFIG_FB_S3 is not set\n# CONFIG_FB_SAVAGE is not set\n# CONFIG_FB_SIS is not set\n# CONFIG_FB_NEOMAGIC is not set\n# CONFIG_FB_KYRO is not set\n# CONFIG_FB_3DFX is not set\n# CONFIG_FB_VOODOO1 is not set\n# CONFIG_FB_VT8623 is not set\n# CONFIG_FB_TRIDENT is not set\n# CONFIG_FB_ARK is not set\n# CONFIG_FB_PM3 is not set\n# CONFIG_FB_CARMINE is not set\n# CONFIG_FB_IBM_GXT4500 is not set\n# CONFIG_FB_VIRTUAL is not set\n# CONFIG_FB_METRONOME is not set\n# CONFIG_FB_MB862XX is not set\n# CONFIG_FB_SIMPLE is not set\n# CONFIG_FB_SSD1307 is not set\n# CONFIG_FB_SM712 is not set\n# end of Frame buffer Devices\n\n#\n# Backlight & LCD device support\n#\nCONFIG_LCD_CLASS_DEVICE=y\n# CONFIG_LCD_PLATFORM is not set\nCONFIG_BACKLIGHT_CLASS_DEVICE=y\n# CONFIG_BACKLIGHT_KTD253 is not set\n# CONFIG_BACKLIGHT_QCOM_WLED is not set\n# CONFIG_BACKLIGHT_ADP8860 is not set\n# CONFIG_BACKLIGHT_ADP8870 is not set\n# CONFIG_BACKLIGHT_LM3639 is not set\n# CONFIG_BACKLIGHT_GPIO is not set\n# CONFIG_BACKLIGHT_LV5207LP is not set\n# CONFIG_BACKLIGHT_BD6107 is not set\n# CONFIG_BACKLIGHT_ARCXCNN is not set\n# end of Backlight & LCD device support\n\nCONFIG_HDMI=y\n\n#\n# Console display driver support\n#\nCONFIG_DUMMY_CONSOLE=y\nCONFIG_DUMMY_CONSOLE_COLUMNS=80\nCONFIG_DUMMY_CONSOLE_ROWS=25\nCONFIG_FRAMEBUFFER_CONSOLE=y\n# CONFIG_FRAMEBUFFER_CONSOLE_LEGACY_ACCELERATION is not set\nCONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y\n# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set\n# CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set\n# end of Console display driver support\n\n# CONFIG_LOGO is not set\n# end of Graphics support\n\n# CONFIG_SOUND is not set\n\n#\n# HID support\n#\nCONFIG_HID=y\n# CONFIG_HID_BATTERY_STRENGTH is not set\nCONFIG_HIDRAW=y\nCONFIG_UHID=y\nCONFIG_HID_GENERIC=y\n\n#\n# Special HID drivers\n#\n# CONFIG_HID_A4TECH is not set\n# CONFIG_HID_ACRUX is not set\n# CONFIG_HID_AUREAL is not set\n# CONFIG_HID_BELKIN is not set\n# CONFIG_HID_CHERRY is not set\n# CONFIG_HID_COUGAR is not set\n# CONFIG_HID_MACALLY is not set\n# CONFIG_HID_CMEDIA is not set\n# CONFIG_HID_CYPRESS is not set\n# CONFIG_HID_DRAGONRISE is not set\n# CONFIG_HID_EMS_FF is not set\n# CONFIG_HID_ELECOM is not set\n# CONFIG_HID_EZKEY is not set\n# CONFIG_HID_GEMBIRD is not set\n# CONFIG_HID_GFRM is not set\n# CONFIG_HID_GLORIOUS is not set\n# CONFIG_HID_VIVALDI is not set\n# CONFIG_HID_KEYTOUCH is not set\n# CONFIG_HID_KYE is not set\n# CONFIG_HID_WALTOP is not set\n# CONFIG_HID_VIEWSONIC is not set\n# CONFIG_HID_VRC2 is not set\n# CONFIG_HID_XIAOMI is not set\n# CONFIG_HID_GYRATION is not set\n# CONFIG_HID_ICADE is not set\n# CONFIG_HID_ITE is not set\n# CONFIG_HID_JABRA is not set\n# CONFIG_HID_TWINHAN is not set\n# CONFIG_HID_KENSINGTON is not set\n# CONFIG_HID_LCPOWER is not set\n# CONFIG_HID_LENOVO is not set\n# CONFIG_HID_MAGICMOUSE is not set\n# CONFIG_HID_MALTRON is not set\n# CONFIG_HID_MAYFLASH is not set\nCONFIG_HID_REDRAGON=y\n# CONFIG_HID_MICROSOFT is not set\n# CONFIG_HID_MONTEREY is not set\n# CONFIG_HID_MULTITOUCH is not set\n# CONFIG_HID_NTI is not set\n# CONFIG_HID_ORTEK is not set\n# CONFIG_HID_PANTHERLORD is not set\n# CONFIG_HID_PETALYNX is not set\n# CONFIG_HID_PICOLCD is not set\n# CONFIG_HID_PLANTRONICS is not set\n# CONFIG_HID_PXRC is not set\n# CONFIG_HID_RAZER is not set\n# CONFIG_HID_PRIMAX is not set\n# CONFIG_HID_SAITEK is not set\n# CONFIG_HID_SEMITEK is not set\n# CONFIG_HID_SPEEDLINK is not set\n# CONFIG_HID_STEAM is not set\n# CONFIG_HID_STEELSERIES is not set\n# CONFIG_HID_SUNPLUS is not set\n# CONFIG_HID_RMI is not set\n# CONFIG_HID_GREENASIA is not set\n# CONFIG_HID_SMARTJOYPLUS is not set\n# CONFIG_HID_TIVO is not set\n# CONFIG_HID_TOPSEED is not set\n# CONFIG_HID_TOPRE is not set\n# CONFIG_HID_UDRAW_PS3 is not set\n# CONFIG_HID_XINMO is not set\n# CONFIG_HID_ZEROPLUS is not set\n# CONFIG_HID_ZYDACRON is not set\n# CONFIG_HID_SENSOR_HUB is not set\n# CONFIG_HID_ALPS is not set\n# end of Special HID drivers\n\n#\n# I2C HID support\n#\n# CONFIG_I2C_HID_ACPI is not set\n# CONFIG_I2C_HID_OF is not set\n# CONFIG_I2C_HID_OF_ELAN is not set\n# CONFIG_I2C_HID_OF_GOODIX is not set\n# end of I2C HID support\n# end of HID support\n\nCONFIG_USB_OHCI_LITTLE_ENDIAN=y\n# CONFIG_USB_SUPPORT is not set\n# CONFIG_MMC is not set\n# CONFIG_MEMSTICK is not set\n# CONFIG_NEW_LEDS is not set\n# CONFIG_ACCESSIBILITY is not set\n# CONFIG_INFINIBAND is not set\nCONFIG_EDAC_SUPPORT=y\n# CONFIG_EDAC is not set\nCONFIG_RTC_LIB=y\nCONFIG_RTC_CLASS=y\nCONFIG_RTC_HCTOSYS=y\nCONFIG_RTC_HCTOSYS_DEVICE=\"rtc0\"\nCONFIG_RTC_SYSTOHC=y\nCONFIG_RTC_SYSTOHC_DEVICE=\"rtc0\"\n# CONFIG_RTC_DEBUG is not set\nCONFIG_RTC_NVMEM=y\n\n#\n# RTC interfaces\n#\nCONFIG_RTC_INTF_SYSFS=y\nCONFIG_RTC_INTF_PROC=y\nCONFIG_RTC_INTF_DEV=y\n# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set\n# CONFIG_RTC_DRV_TEST is not set\n\n#\n# I2C RTC drivers\n#\n# CONFIG_RTC_DRV_ABB5ZES3 is not set\n# CONFIG_RTC_DRV_ABEOZ9 is not set\n# CONFIG_RTC_DRV_ABX80X is not set\n# CONFIG_RTC_DRV_DS1307 is not set\n# CONFIG_RTC_DRV_DS1374 is not set\n# CONFIG_RTC_DRV_DS1672 is not set\n# CONFIG_RTC_DRV_HYM8563 is not set\n# CONFIG_RTC_DRV_MAX6900 is not set\n# CONFIG_RTC_DRV_NCT3018Y is not set\n# CONFIG_RTC_DRV_RS5C372 is not set\n# CONFIG_RTC_DRV_ISL1208 is not set\n# CONFIG_RTC_DRV_ISL12022 is not set\n# CONFIG_RTC_DRV_ISL12026 is not set\n# CONFIG_RTC_DRV_X1205 is not set\n# CONFIG_RTC_DRV_PCF8523 is not set\n# CONFIG_RTC_DRV_PCF85063 is not set\n# CONFIG_RTC_DRV_PCF85363 is not set\n# CONFIG_RTC_DRV_PCF8563 is not set\n# CONFIG_RTC_DRV_PCF8583 is not set\n# CONFIG_RTC_DRV_M41T80 is not set\n# CONFIG_RTC_DRV_BQ32K is not set\n# CONFIG_RTC_DRV_S35390A is not set\n# CONFIG_RTC_DRV_FM3130 is not set\n# CONFIG_RTC_DRV_RX8010 is not set\n# CONFIG_RTC_DRV_RX8581 is not set\n# CONFIG_RTC_DRV_RX8025 is not set\n# CONFIG_RTC_DRV_EM3027 is not set\n# CONFIG_RTC_DRV_RV3028 is not set\n# CONFIG_RTC_DRV_RV3032 is not set\n# CONFIG_RTC_DRV_RV8803 is not set\n# CONFIG_RTC_DRV_SD3078 is not set\n\n#\n# SPI RTC drivers\n#\nCONFIG_RTC_I2C_AND_SPI=y\n\n#\n# SPI and I2C RTC drivers\n#\n# CONFIG_RTC_DRV_DS3232 is not set\n# CONFIG_RTC_DRV_PCF2127 is not set\n# CONFIG_RTC_DRV_RV3029C2 is not set\n# CONFIG_RTC_DRV_RX6110 is not set\n\n#\n# Platform RTC drivers\n#\n# CONFIG_RTC_DRV_DS1286 is not set\n# CONFIG_RTC_DRV_DS1511 is not set\n# CONFIG_RTC_DRV_DS1553 is not set\n# CONFIG_RTC_DRV_DS1685_FAMILY is not set\n# CONFIG_RTC_DRV_DS1742 is not set\n# CONFIG_RTC_DRV_DS2404 is not set\n# CONFIG_RTC_DRV_EFI is not set\n# CONFIG_RTC_DRV_STK17TA8 is not set\n# CONFIG_RTC_DRV_M48T86 is not set\n# CONFIG_RTC_DRV_M48T35 is not set\n# CONFIG_RTC_DRV_M48T59 is not set\n# CONFIG_RTC_DRV_MSM6242 is not set\n# CONFIG_RTC_DRV_BQ4802 is not set\n# CONFIG_RTC_DRV_RP5C01 is not set\n# CONFIG_RTC_DRV_V3020 is not set\n# CONFIG_RTC_DRV_ZYNQMP is not set\n\n#\n# on-CPU RTC drivers\n#\nCONFIG_RTC_DRV_PL030=y\nCONFIG_RTC_DRV_PL031=y\n# CONFIG_RTC_DRV_CADENCE is not set\n# CONFIG_RTC_DRV_FTRTC010 is not set\n# CONFIG_RTC_DRV_R7301 is not set\n\n#\n# HID Sensor RTC drivers\n#\n# CONFIG_RTC_DRV_GOLDFISH is not set\nCONFIG_DMADEVICES=y\n# CONFIG_DMADEVICES_DEBUG is not set\n\n#\n# DMA Devices\n#\nCONFIG_DMA_ENGINE=y\nCONFIG_DMA_ACPI=y\nCONFIG_DMA_OF=y\n# CONFIG_ALTERA_MSGDMA is not set\n# CONFIG_AMBA_PL08X is not set\n# CONFIG_DW_AXI_DMAC is not set\n# CONFIG_FSL_EDMA is not set\n# CONFIG_FSL_QDMA is not set\n# CONFIG_INTEL_IDMA64 is not set\n# CONFIG_MV_XOR_V2 is not set\n# CONFIG_PL330_DMA is not set\n# CONFIG_PLX_DMA is not set\n# CONFIG_XILINX_DMA is not set\n# CONFIG_XILINX_ZYNQMP_DMA is not set\n# CONFIG_XILINX_ZYNQMP_DPDMA is not set\n# CONFIG_QCOM_HIDMA_MGMT is not set\n# CONFIG_QCOM_HIDMA is not set\n# CONFIG_DW_DMAC is not set\n# CONFIG_DW_DMAC_PCI is not set\n# CONFIG_DW_EDMA is not set\n# CONFIG_DW_EDMA_PCIE is not set\n# CONFIG_SF_PDMA is not set\n\n#\n# DMA Clients\n#\n# CONFIG_ASYNC_TX_DMA is not set\n# CONFIG_DMATEST is not set\n\n#\n# DMABUF options\n#\nCONFIG_SYNC_FILE=y\n# CONFIG_SW_SYNC is not set\n# CONFIG_UDMABUF is not set\n# CONFIG_DMABUF_MOVE_NOTIFY is not set\n# CONFIG_DMABUF_DEBUG is not set\n# CONFIG_DMABUF_SELFTESTS is not set\n# CONFIG_DMABUF_HEAPS is not set\n# CONFIG_DMABUF_SYSFS_STATS is not set\n# end of DMABUF options\n\n# CONFIG_AUXDISPLAY is not set\nCONFIG_UIO=y\n# CONFIG_UIO_CIF is not set\nCONFIG_UIO_PDRV_GENIRQ=y\nCONFIG_UIO_DMEM_GENIRQ=y\n# CONFIG_UIO_AEC is not set\n# CONFIG_UIO_SERCOS3 is not set\n# CONFIG_UIO_PCI_GENERIC is not set\n# CONFIG_UIO_NETX is not set\n# CONFIG_UIO_PRUSS is not set\n# CONFIG_UIO_MF624 is not set\nCONFIG_VFIO=y\nCONFIG_VFIO_IOMMU_TYPE1=y\nCONFIG_VFIO_VIRQFD=y\n# CONFIG_VFIO_NOIOMMU is not set\nCONFIG_VFIO_PCI_CORE=y\nCONFIG_VFIO_PCI_MMAP=y\nCONFIG_VFIO_PCI_INTX=y\nCONFIG_VFIO_PCI=y\n# CONFIG_VFIO_PLATFORM is not set\n# CONFIG_VFIO_MDEV is not set\n# CONFIG_VIRT_DRIVERS is not set\nCONFIG_VIRTIO_ANCHOR=y\nCONFIG_VIRTIO=y\nCONFIG_VIRTIO_PCI_LIB=y\nCONFIG_VIRTIO_PCI_LIB_LEGACY=y\nCONFIG_VIRTIO_MENU=y\nCONFIG_VIRTIO_PCI=y\nCONFIG_VIRTIO_PCI_LEGACY=y\nCONFIG_VIRTIO_PMEM=y\nCONFIG_VIRTIO_BALLOON=y\n# CONFIG_VIRTIO_MEM is not set\nCONFIG_VIRTIO_INPUT=y\nCONFIG_VIRTIO_MMIO=y\nCONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y\nCONFIG_VIRTIO_DMA_SHARED_BUFFER=y\n# CONFIG_VDPA is not set\nCONFIG_VHOST_MENU=y\n# CONFIG_VHOST_NET is not set\n# CONFIG_VHOST_VSOCK is not set\n# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set\n\n#\n# Microsoft Hyper-V guest support\n#\n# CONFIG_HYPERV is not set\n# end of Microsoft Hyper-V guest support\n\n# CONFIG_GREYBUS is not set\n# CONFIG_COMEDI is not set\n# CONFIG_STAGING is not set\n# CONFIG_GOLDFISH is not set\n# CONFIG_CHROME_PLATFORMS is not set\n# CONFIG_MELLANOX_PLATFORM is not set\n# CONFIG_SURFACE_PLATFORMS is not set\nCONFIG_HAVE_CLK=y\nCONFIG_HAVE_CLK_PREPARE=y\nCONFIG_COMMON_CLK=y\n\n#\n# Clock driver for ARM Reference designs\n#\n# CONFIG_CLK_ICST is not set\n# CONFIG_CLK_SP810 is not set\n# end of Clock driver for ARM Reference designs\n\n# CONFIG_COMMON_CLK_MAX9485 is not set\n# CONFIG_COMMON_CLK_SI5341 is not set\n# CONFIG_COMMON_CLK_SI5351 is not set\n# CONFIG_COMMON_CLK_SI514 is not set\n# CONFIG_COMMON_CLK_SI544 is not set\n# CONFIG_COMMON_CLK_SI570 is not set\n# CONFIG_COMMON_CLK_CDCE706 is not set\n# CONFIG_COMMON_CLK_CDCE925 is not set\n# CONFIG_COMMON_CLK_CS2000_CP is not set\n# CONFIG_COMMON_CLK_AXI_CLKGEN is not set\n# CONFIG_COMMON_CLK_XGENE is not set\n# CONFIG_COMMON_CLK_RS9_PCIE is not set\n# CONFIG_COMMON_CLK_VC5 is not set\n# CONFIG_COMMON_CLK_VC7 is not set\n# CONFIG_COMMON_CLK_FIXED_MMIO is not set\n# CONFIG_XILINX_VCU is not set\n# CONFIG_COMMON_CLK_XLNX_CLKWZRD is not set\n# CONFIG_HWSPINLOCK is not set\n\n#\n# Clock Source drivers\n#\nCONFIG_TIMER_OF=y\nCONFIG_TIMER_ACPI=y\nCONFIG_TIMER_PROBE=y\nCONFIG_ARM_ARCH_TIMER=y\nCONFIG_ARM_ARCH_TIMER_EVTSTREAM=y\nCONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND=y\nCONFIG_FSL_ERRATUM_A008585=y\nCONFIG_HISILICON_ERRATUM_161010101=y\nCONFIG_ARM64_ERRATUM_858921=y\n# CONFIG_MICROCHIP_PIT64B is not set\n# end of Clock Source drivers\n\nCONFIG_MAILBOX=y\n# CONFIG_ARM_MHU is not set\n# CONFIG_ARM_MHU_V2 is not set\n# CONFIG_PLATFORM_MHU is not set\n# CONFIG_PL320_MBOX is not set\nCONFIG_PCC=y\n# CONFIG_ALTERA_MBOX is not set\n# CONFIG_MAILBOX_TEST is not set\nCONFIG_IOMMU_IOVA=y\nCONFIG_IOMMU_API=y\nCONFIG_IOMMU_SUPPORT=y\n\n#\n# Generic IOMMU Pagetable Support\n#\nCONFIG_IOMMU_IO_PGTABLE=y\nCONFIG_IOMMU_IO_PGTABLE_LPAE=y\n# CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST is not set\n# CONFIG_IOMMU_IO_PGTABLE_ARMV7S is not set\n# CONFIG_IOMMU_IO_PGTABLE_DART is not set\n# end of Generic IOMMU Pagetable Support\n\n# CONFIG_IOMMU_DEBUGFS is not set\nCONFIG_IOMMU_DEFAULT_DMA_STRICT=y\n# CONFIG_IOMMU_DEFAULT_DMA_LAZY is not set\n# CONFIG_IOMMU_DEFAULT_PASSTHROUGH is not set\nCONFIG_OF_IOMMU=y\nCONFIG_IOMMU_DMA=y\n# CONFIG_ARM_SMMU is not set\n# CONFIG_ARM_SMMU_V3 is not set\nCONFIG_VIRTIO_IOMMU=y\n\n#\n# Remoteproc drivers\n#\n# CONFIG_REMOTEPROC is not set\n# end of Remoteproc drivers\n\n#\n# Rpmsg drivers\n#\n# CONFIG_RPMSG_QCOM_GLINK_RPM is not set\n# CONFIG_RPMSG_VIRTIO is not set\n# end of Rpmsg drivers\n\n# CONFIG_SOUNDWIRE is not set\n\n#\n# SOC (System On Chip) specific Drivers\n#\n\n#\n# Amlogic SoC drivers\n#\n# end of Amlogic SoC drivers\n\n#\n# Broadcom SoC drivers\n#\n# CONFIG_SOC_BRCMSTB is not set\n# end of Broadcom SoC drivers\n\n#\n# NXP/Freescale QorIQ SoC drivers\n#\n# CONFIG_QUICC_ENGINE is not set\n# CONFIG_FSL_RCPM is not set\n# end of NXP/Freescale QorIQ SoC drivers\n\n#\n# fujitsu SoC drivers\n#\n# CONFIG_A64FX_DIAG is not set\n# end of fujitsu SoC drivers\n\n#\n# i.MX SoC drivers\n#\n# end of i.MX SoC drivers\n\n#\n# Enable LiteX SoC Builder specific drivers\n#\n# CONFIG_LITEX_SOC_CONTROLLER is not set\n# end of Enable LiteX SoC Builder specific drivers\n\n#\n# Qualcomm SoC drivers\n#\n# end of Qualcomm SoC drivers\n\n# CONFIG_SOC_TI is not set\n\n#\n# Xilinx SoC drivers\n#\n# end of Xilinx SoC drivers\n# end of SOC (System On Chip) specific Drivers\n\n# CONFIG_PM_DEVFREQ is not set\n# CONFIG_EXTCON is not set\n# CONFIG_MEMORY is not set\n# CONFIG_IIO is not set\n# CONFIG_NTB is not set\n# CONFIG_PWM is not set\n\n#\n# IRQ chip support\n#\nCONFIG_IRQCHIP=y\nCONFIG_ARM_GIC=y\nCONFIG_ARM_GIC_MAX_NR=1\nCONFIG_ARM_GIC_V2M=y\nCONFIG_ARM_GIC_V3=y\nCONFIG_ARM_GIC_V3_ITS=y\nCONFIG_ARM_GIC_V3_ITS_PCI=y\n# CONFIG_AL_FIC is not set\n# CONFIG_XILINX_INTC is not set\nCONFIG_PARTITION_PERCPU=y\n# end of IRQ chip support\n\n# CONFIG_IPACK_BUS is not set\n# CONFIG_RESET_CONTROLLER is not set\n\n#\n# PHY Subsystem\n#\n# CONFIG_GENERIC_PHY is not set\n# CONFIG_PHY_XGENE is not set\n# CONFIG_PHY_CAN_TRANSCEIVER is not set\n\n#\n# PHY drivers for Broadcom platforms\n#\n# CONFIG_BCM_KONA_USB2_PHY is not set\n# end of PHY drivers for Broadcom platforms\n\n# CONFIG_PHY_CADENCE_TORRENT is not set\n# CONFIG_PHY_CADENCE_DPHY is not set\n# CONFIG_PHY_CADENCE_DPHY_RX is not set\n# CONFIG_PHY_CADENCE_SALVO is not set\n# CONFIG_PHY_PXA_28NM_HSIC is not set\n# CONFIG_PHY_PXA_28NM_USB2 is not set\n# end of PHY Subsystem\n\n# CONFIG_POWERCAP is not set\n# CONFIG_MCB is not set\n\n#\n# Performance monitor support\n#\n# CONFIG_ARM_CCI_PMU is not set\n# CONFIG_ARM_CCN is not set\n# CONFIG_ARM_CMN is not set\nCONFIG_ARM_PMU=y\nCONFIG_ARM_PMU_ACPI=y\n# CONFIG_ARM_SMMU_V3_PMU is not set\n# CONFIG_ARM_DSU_PMU is not set\n# CONFIG_ARM_SPE_PMU is not set\n# CONFIG_ARM_DMC620_PMU is not set\n# CONFIG_ALIBABA_UNCORE_DRW_PMU is not set\n# CONFIG_HISI_PMU is not set\n# CONFIG_HISI_PCIE_PMU is not set\n# CONFIG_HNS3_PMU is not set\n# end of Performance monitor support\n\nCONFIG_RAS=y\n# CONFIG_USB4 is not set\n\n#\n# Android\n#\n# CONFIG_ANDROID_BINDER_IPC is not set\n# end of Android\n\nCONFIG_LIBNVDIMM=y\nCONFIG_BLK_DEV_PMEM=y\nCONFIG_ND_CLAIM=y\nCONFIG_ND_BTT=y\nCONFIG_BTT=y\nCONFIG_ND_PFN=y\nCONFIG_NVDIMM_PFN=y\nCONFIG_NVDIMM_DAX=y\nCONFIG_OF_PMEM=y\nCONFIG_DAX=y\nCONFIG_DEV_DAX=y\nCONFIG_DEV_DAX_PMEM=y\nCONFIG_DEV_DAX_KMEM=y\nCONFIG_NVMEM=y\nCONFIG_NVMEM_SYSFS=y\n# CONFIG_NVMEM_RMEM is not set\n\n#\n# HW tracing support\n#\n# CONFIG_STM is not set\n# CONFIG_INTEL_TH is not set\n# CONFIG_HISI_PTT is not set\n# end of HW tracing support\n\n# CONFIG_FPGA is not set\n# CONFIG_FSI is not set\n# CONFIG_TEE is not set\n# CONFIG_SIOX is not set\n# CONFIG_SLIMBUS is not set\n# CONFIG_INTERCONNECT is not set\n# CONFIG_COUNTER is not set\n# CONFIG_MOST is not set\n# CONFIG_PECI is not set\n# CONFIG_HTE is not set\n# end of Device Drivers\n\n#\n# File systems\n#\nCONFIG_DCACHE_WORD_ACCESS=y\n# CONFIG_VALIDATE_FS_PARSER is not set\nCONFIG_FS_IOMAP=y\n# CONFIG_EXT2_FS is not set\n# CONFIG_EXT3_FS is not set\nCONFIG_EXT4_FS=y\nCONFIG_EXT4_USE_FOR_EXT2=y\nCONFIG_EXT4_FS_POSIX_ACL=y\nCONFIG_EXT4_FS_SECURITY=y\nCONFIG_EXT4_DEBUG=y\nCONFIG_JBD2=y\nCONFIG_JBD2_DEBUG=y\nCONFIG_FS_MBCACHE=y\n# CONFIG_REISERFS_FS is not set\n# CONFIG_JFS_FS is not set\n# CONFIG_XFS_FS is not set\n# CONFIG_GFS2_FS is not set\n# CONFIG_OCFS2_FS is not set\n# CONFIG_BTRFS_FS is not set\n# CONFIG_NILFS2_FS is not set\n# CONFIG_F2FS_FS is not set\nCONFIG_FS_DAX=y\nCONFIG_FS_DAX_PMD=y\nCONFIG_FS_POSIX_ACL=y\nCONFIG_EXPORTFS=y\n# CONFIG_EXPORTFS_BLOCK_OPS is not set\nCONFIG_FILE_LOCKING=y\nCONFIG_FS_ENCRYPTION=y\nCONFIG_FS_ENCRYPTION_ALGS=y\n# CONFIG_FS_VERITY is not set\nCONFIG_FSNOTIFY=y\nCONFIG_DNOTIFY=y\nCONFIG_INOTIFY_USER=y\nCONFIG_FANOTIFY=y\n# CONFIG_QUOTA is not set\nCONFIG_AUTOFS4_FS=y\nCONFIG_AUTOFS_FS=y\nCONFIG_FUSE_FS=y\nCONFIG_CUSE=y\nCONFIG_VIRTIO_FS=y\nCONFIG_FUSE_DAX=y\nCONFIG_OVERLAY_FS=y\n# CONFIG_OVERLAY_FS_REDIRECT_DIR is not set\nCONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW=y\n# CONFIG_OVERLAY_FS_INDEX is not set\n# CONFIG_OVERLAY_FS_XINO_AUTO is not set\n# CONFIG_OVERLAY_FS_METACOPY is not set\n\n#\n# Caches\n#\nCONFIG_NETFS_SUPPORT=y\n# CONFIG_NETFS_STATS is not set\nCONFIG_FSCACHE=y\n# CONFIG_FSCACHE_STATS is not set\n# CONFIG_FSCACHE_DEBUG is not set\nCONFIG_CACHEFILES=y\n# CONFIG_CACHEFILES_DEBUG is not set\n# CONFIG_CACHEFILES_ERROR_INJECTION is not set\n# CONFIG_CACHEFILES_ONDEMAND is not set\n# end of Caches\n\n#\n# CD-ROM/DVD Filesystems\n#\nCONFIG_ISO9660_FS=y\nCONFIG_JOLIET=y\nCONFIG_ZISOFS=y\nCONFIG_UDF_FS=y\n# end of CD-ROM/DVD Filesystems\n\n#\n# DOS/FAT/EXFAT/NT Filesystems\n#\nCONFIG_FAT_FS=y\nCONFIG_MSDOS_FS=y\nCONFIG_VFAT_FS=y\nCONFIG_FAT_DEFAULT_CODEPAGE=437\nCONFIG_FAT_DEFAULT_IOCHARSET=\"ascii\"\n# CONFIG_FAT_DEFAULT_UTF8 is not set\n# CONFIG_EXFAT_FS is not set\n# CONFIG_NTFS_FS is not set\n# CONFIG_NTFS3_FS is not set\n# end of DOS/FAT/EXFAT/NT Filesystems\n\n#\n# Pseudo filesystems\n#\nCONFIG_PROC_FS=y\nCONFIG_PROC_KCORE=y\nCONFIG_PROC_SYSCTL=y\nCONFIG_PROC_PAGE_MONITOR=y\nCONFIG_PROC_CHILDREN=y\nCONFIG_KERNFS=y\nCONFIG_SYSFS=y\nCONFIG_TMPFS=y\nCONFIG_TMPFS_POSIX_ACL=y\nCONFIG_TMPFS_XATTR=y\n# CONFIG_TMPFS_INODE64 is not set\nCONFIG_ARCH_SUPPORTS_HUGETLBFS=y\nCONFIG_HUGETLBFS=y\nCONFIG_HUGETLB_PAGE=y\nCONFIG_MEMFD_CREATE=y\nCONFIG_ARCH_HAS_GIGANTIC_PAGE=y\nCONFIG_CONFIGFS_FS=y\nCONFIG_EFIVAR_FS=y\n# end of Pseudo filesystems\n\nCONFIG_MISC_FILESYSTEMS=y\n# CONFIG_ORANGEFS_FS is not set\n# CONFIG_ADFS_FS is not set\n# CONFIG_AFFS_FS is not set\n# CONFIG_ECRYPT_FS is not set\n# CONFIG_HFS_FS is not set\n# CONFIG_HFSPLUS_FS is not set\n# CONFIG_BEFS_FS is not set\n# CONFIG_BFS_FS is not set\n# CONFIG_EFS_FS is not set\n# CONFIG_CRAMFS is not set\nCONFIG_SQUASHFS=y\nCONFIG_SQUASHFS_FILE_CACHE=y\n# CONFIG_SQUASHFS_FILE_DIRECT is not set\nCONFIG_SQUASHFS_DECOMP_SINGLE=y\n# CONFIG_SQUASHFS_DECOMP_MULTI is not set\n# CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU is not set\n# CONFIG_SQUASHFS_XATTR is not set\nCONFIG_SQUASHFS_ZLIB=y\n# CONFIG_SQUASHFS_LZ4 is not set\n# CONFIG_SQUASHFS_LZO is not set\nCONFIG_SQUASHFS_XZ=y\n# CONFIG_SQUASHFS_ZSTD is not set\n# CONFIG_SQUASHFS_4K_DEVBLK_SIZE is not set\n# CONFIG_SQUASHFS_EMBEDDED is not set\nCONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3\n# CONFIG_VXFS_FS is not set\n# CONFIG_MINIX_FS is not set\n# CONFIG_OMFS_FS is not set\n# CONFIG_HPFS_FS is not set\n# CONFIG_QNX4FS_FS is not set\n# CONFIG_QNX6FS_FS is not set\n# CONFIG_ROMFS_FS is not set\n# CONFIG_PSTORE is not set\n# CONFIG_SYSV_FS is not set\n# CONFIG_UFS_FS is not set\n# CONFIG_EROFS_FS is not set\nCONFIG_NETWORK_FILESYSTEMS=y\nCONFIG_NFS_FS=y\nCONFIG_NFS_V2=y\nCONFIG_NFS_V3=y\n# CONFIG_NFS_V3_ACL is not set\nCONFIG_NFS_V4=y\n# CONFIG_NFS_SWAP is not set\n# CONFIG_NFS_V4_1 is not set\n# CONFIG_ROOT_NFS is not set\n# CONFIG_NFS_FSCACHE is not set\n# CONFIG_NFS_USE_LEGACY_DNS is not set\nCONFIG_NFS_USE_KERNEL_DNS=y\nCONFIG_NFS_DISABLE_UDP_SUPPORT=y\nCONFIG_NFSD=y\n# CONFIG_NFSD_V3_ACL is not set\nCONFIG_NFSD_V4=y\n# CONFIG_NFSD_BLOCKLAYOUT is not set\n# CONFIG_NFSD_SCSILAYOUT is not set\n# CONFIG_NFSD_FLEXFILELAYOUT is not set\nCONFIG_GRACE_PERIOD=y\nCONFIG_LOCKD=y\nCONFIG_LOCKD_V4=y\nCONFIG_NFS_COMMON=y\nCONFIG_SUNRPC=y\nCONFIG_SUNRPC_GSS=y\nCONFIG_RPCSEC_GSS_KRB5=y\n# CONFIG_SUNRPC_DISABLE_INSECURE_ENCTYPES is not set\n# CONFIG_SUNRPC_DEBUG is not set\n# CONFIG_CEPH_FS is not set\n# CONFIG_CIFS is not set\n# CONFIG_SMB_SERVER is not set\n# CONFIG_CODA_FS is not set\n# CONFIG_AFS_FS is not set\nCONFIG_NLS=y\nCONFIG_NLS_DEFAULT=\"utf8\"\nCONFIG_NLS_CODEPAGE_437=y\nCONFIG_NLS_CODEPAGE_737=y\nCONFIG_NLS_CODEPAGE_775=y\nCONFIG_NLS_CODEPAGE_850=y\nCONFIG_NLS_CODEPAGE_852=y\nCONFIG_NLS_CODEPAGE_855=y\nCONFIG_NLS_CODEPAGE_857=y\nCONFIG_NLS_CODEPAGE_860=y\nCONFIG_NLS_CODEPAGE_861=y\nCONFIG_NLS_CODEPAGE_862=y\nCONFIG_NLS_CODEPAGE_863=y\nCONFIG_NLS_CODEPAGE_864=y\nCONFIG_NLS_CODEPAGE_865=y\nCONFIG_NLS_CODEPAGE_866=y\nCONFIG_NLS_CODEPAGE_869=y\nCONFIG_NLS_CODEPAGE_936=y\nCONFIG_NLS_CODEPAGE_950=y\nCONFIG_NLS_CODEPAGE_932=y\nCONFIG_NLS_CODEPAGE_949=y\nCONFIG_NLS_CODEPAGE_874=y\nCONFIG_NLS_ISO8859_8=y\nCONFIG_NLS_CODEPAGE_1250=y\nCONFIG_NLS_CODEPAGE_1251=y\nCONFIG_NLS_ASCII=y\nCONFIG_NLS_ISO8859_1=y\nCONFIG_NLS_ISO8859_2=y\nCONFIG_NLS_ISO8859_3=y\nCONFIG_NLS_ISO8859_4=y\nCONFIG_NLS_ISO8859_5=y\nCONFIG_NLS_ISO8859_6=y\nCONFIG_NLS_ISO8859_7=y\nCONFIG_NLS_ISO8859_9=y\nCONFIG_NLS_ISO8859_13=y\nCONFIG_NLS_ISO8859_14=y\nCONFIG_NLS_ISO8859_15=y\nCONFIG_NLS_KOI8_R=y\nCONFIG_NLS_KOI8_U=y\nCONFIG_NLS_MAC_ROMAN=y\nCONFIG_NLS_MAC_CELTIC=y\nCONFIG_NLS_MAC_CENTEURO=y\nCONFIG_NLS_MAC_CROATIAN=y\nCONFIG_NLS_MAC_CYRILLIC=y\nCONFIG_NLS_MAC_GAELIC=y\nCONFIG_NLS_MAC_GREEK=y\nCONFIG_NLS_MAC_ICELAND=y\nCONFIG_NLS_MAC_INUIT=y\nCONFIG_NLS_MAC_ROMANIAN=y\nCONFIG_NLS_MAC_TURKISH=y\nCONFIG_NLS_UTF8=y\n# CONFIG_DLM is not set\n# CONFIG_UNICODE is not set\nCONFIG_IO_WQ=y\n# end of File systems\n\n#\n# Security options\n#\nCONFIG_KEYS=y\n# CONFIG_KEYS_REQUEST_CACHE is not set\nCONFIG_PERSISTENT_KEYRINGS=y\n# CONFIG_BIG_KEYS is not set\n# CONFIG_TRUSTED_KEYS is not set\n# CONFIG_ENCRYPTED_KEYS is not set\n# CONFIG_KEY_DH_OPERATIONS is not set\n# CONFIG_SECURITY_DMESG_RESTRICT is not set\n# CONFIG_SECURITY is not set\nCONFIG_SECURITYFS=y\nCONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y\n# CONFIG_HARDENED_USERCOPY is not set\nCONFIG_FORTIFY_SOURCE=y\n# CONFIG_STATIC_USERMODEHELPER is not set\n# CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set\nCONFIG_DEFAULT_SECURITY_DAC=y\nCONFIG_LSM=\"yama,loadpin,safesetid,integrity\"\n\n#\n# Kernel hardening options\n#\n\n#\n# Memory initialization\n#\nCONFIG_INIT_STACK_NONE=y\n# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set\n# CONFIG_INIT_ON_FREE_DEFAULT_ON is not set\n# end of Memory initialization\n\nCONFIG_RANDSTRUCT_NONE=y\n# end of Kernel hardening options\n# end of Security options\n\nCONFIG_CRYPTO=y\n\n#\n# Crypto core or helper\n#\n# CONFIG_CRYPTO_FIPS is not set\nCONFIG_CRYPTO_ALGAPI=y\nCONFIG_CRYPTO_ALGAPI2=y\nCONFIG_CRYPTO_AEAD=y\nCONFIG_CRYPTO_AEAD2=y\nCONFIG_CRYPTO_SKCIPHER=y\nCONFIG_CRYPTO_SKCIPHER2=y\nCONFIG_CRYPTO_HASH=y\nCONFIG_CRYPTO_HASH2=y\nCONFIG_CRYPTO_RNG=y\nCONFIG_CRYPTO_RNG2=y\nCONFIG_CRYPTO_RNG_DEFAULT=y\nCONFIG_CRYPTO_AKCIPHER2=y\nCONFIG_CRYPTO_AKCIPHER=y\nCONFIG_CRYPTO_KPP2=y\nCONFIG_CRYPTO_ACOMP2=y\nCONFIG_CRYPTO_MANAGER=y\nCONFIG_CRYPTO_MANAGER2=y\n# CONFIG_CRYPTO_USER is not set\n# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set\n# CONFIG_CRYPTO_MANAGER_EXTRA_TESTS is not set\nCONFIG_CRYPTO_GF128MUL=y\nCONFIG_CRYPTO_NULL=y\nCONFIG_CRYPTO_NULL2=y\n# CONFIG_CRYPTO_PCRYPT is not set\nCONFIG_CRYPTO_CRYPTD=y\nCONFIG_CRYPTO_AUTHENC=y\n# CONFIG_CRYPTO_TEST is not set\n# end of Crypto core or helper\n\n#\n# Public-key cryptography\n#\n# CONFIG_CRYPTO_RSA is not set\n# CONFIG_CRYPTO_DH is not set\nCONFIG_CRYPTO_ECC=y\n# CONFIG_CRYPTO_ECDH is not set\nCONFIG_CRYPTO_ECDSA=y\n# CONFIG_CRYPTO_ECRDSA is not set\n# CONFIG_CRYPTO_SM2 is not set\n# CONFIG_CRYPTO_CURVE25519 is not set\n# end of Public-key cryptography\n\n#\n# Block ciphers\n#\nCONFIG_CRYPTO_AES=y\n# CONFIG_CRYPTO_AES_TI is not set\n# CONFIG_CRYPTO_ARIA is not set\n# CONFIG_CRYPTO_BLOWFISH is not set\n# CONFIG_CRYPTO_CAMELLIA is not set\n# CONFIG_CRYPTO_CAST5 is not set\n# CONFIG_CRYPTO_CAST6 is not set\nCONFIG_CRYPTO_DES=y\n# CONFIG_CRYPTO_FCRYPT is not set\n# CONFIG_CRYPTO_SERPENT is not set\n# CONFIG_CRYPTO_SM4_GENERIC is not set\n# CONFIG_CRYPTO_TWOFISH is not set\n# end of Block ciphers\n\n#\n# Length-preserving ciphers and modes\n#\n# CONFIG_CRYPTO_ADIANTUM is not set\n# CONFIG_CRYPTO_CHACHA20 is not set\nCONFIG_CRYPTO_CBC=y\n# CONFIG_CRYPTO_CFB is not set\nCONFIG_CRYPTO_CTR=y\nCONFIG_CRYPTO_CTS=y\nCONFIG_CRYPTO_ECB=y\n# CONFIG_CRYPTO_HCTR2 is not set\n# CONFIG_CRYPTO_KEYWRAP is not set\nCONFIG_CRYPTO_LRW=y\n# CONFIG_CRYPTO_OFB is not set\nCONFIG_CRYPTO_PCBC=y\nCONFIG_CRYPTO_XTS=y\n# end of Length-preserving ciphers and modes\n\n#\n# AEAD (authenticated encryption with associated data) ciphers\n#\n# CONFIG_CRYPTO_AEGIS128 is not set\n# CONFIG_CRYPTO_CHACHA20POLY1305 is not set\n# CONFIG_CRYPTO_CCM is not set\nCONFIG_CRYPTO_GCM=y\nCONFIG_CRYPTO_SEQIV=y\nCONFIG_CRYPTO_ECHAINIV=y\n# CONFIG_CRYPTO_ESSIV is not set\n# end of AEAD (authenticated encryption with associated data) ciphers\n\n#\n# Hashes, digests, and MACs\n#\n# CONFIG_CRYPTO_BLAKE2B is not set\nCONFIG_CRYPTO_CMAC=y\nCONFIG_CRYPTO_GHASH=y\nCONFIG_CRYPTO_HMAC=y\n# CONFIG_CRYPTO_MD4 is not set\nCONFIG_CRYPTO_MD5=y\n# CONFIG_CRYPTO_MICHAEL_MIC is not set\nCONFIG_CRYPTO_POLY1305=y\n# CONFIG_CRYPTO_RMD160 is not set\nCONFIG_CRYPTO_SHA1=y\nCONFIG_CRYPTO_SHA256=y\nCONFIG_CRYPTO_SHA512=y\n# CONFIG_CRYPTO_SHA3 is not set\n# CONFIG_CRYPTO_SM3_GENERIC is not set\n# CONFIG_CRYPTO_STREEBOG is not set\n# CONFIG_CRYPTO_VMAC is not set\n# CONFIG_CRYPTO_WP512 is not set\n# CONFIG_CRYPTO_XCBC is not set\n# CONFIG_CRYPTO_XXHASH is not set\n# end of Hashes, digests, and MACs\n\n#\n# CRCs (cyclic redundancy checks)\n#\nCONFIG_CRYPTO_CRC32C=y\n# CONFIG_CRYPTO_CRC32 is not set\nCONFIG_CRYPTO_CRCT10DIF=y\n# end of CRCs (cyclic redundancy checks)\n\n#\n# Compression\n#\nCONFIG_CRYPTO_DEFLATE=y\nCONFIG_CRYPTO_LZO=y\n# CONFIG_CRYPTO_842 is not set\n# CONFIG_CRYPTO_LZ4 is not set\n# CONFIG_CRYPTO_LZ4HC is not set\n# CONFIG_CRYPTO_ZSTD is not set\n# end of Compression\n\n#\n# Random number generation\n#\n# CONFIG_CRYPTO_ANSI_CPRNG is not set\nCONFIG_CRYPTO_DRBG_MENU=y\nCONFIG_CRYPTO_DRBG_HMAC=y\nCONFIG_CRYPTO_DRBG_HASH=y\nCONFIG_CRYPTO_DRBG_CTR=y\nCONFIG_CRYPTO_DRBG=y\nCONFIG_CRYPTO_JITTERENTROPY=y\n# end of Random number generation\n\n#\n# Userspace interface\n#\nCONFIG_CRYPTO_USER_API=y\n# CONFIG_CRYPTO_USER_API_HASH is not set\n# CONFIG_CRYPTO_USER_API_SKCIPHER is not set\nCONFIG_CRYPTO_USER_API_RNG=y\n# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set\n# CONFIG_CRYPTO_USER_API_AEAD is not set\n# CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE is not set\n# end of Userspace interface\n\n# CONFIG_CRYPTO_NHPOLY1305_NEON is not set\nCONFIG_CRYPTO_CHACHA20_NEON=y\n\n#\n# Accelerated Cryptographic Algorithms for CPU (arm64)\n#\n# CONFIG_CRYPTO_GHASH_ARM64_CE is not set\nCONFIG_CRYPTO_POLY1305_NEON=y\n# CONFIG_CRYPTO_SHA1_ARM64_CE is not set\n# CONFIG_CRYPTO_SHA256_ARM64 is not set\n# CONFIG_CRYPTO_SHA2_ARM64_CE is not set\n# CONFIG_CRYPTO_SHA512_ARM64 is not set\n# CONFIG_CRYPTO_SHA512_ARM64_CE is not set\n# CONFIG_CRYPTO_SHA3_ARM64 is not set\n# CONFIG_CRYPTO_SM3_NEON is not set\n# CONFIG_CRYPTO_SM3_ARM64_CE is not set\n# CONFIG_CRYPTO_POLYVAL_ARM64_CE is not set\n# CONFIG_CRYPTO_AES_ARM64 is not set\n# CONFIG_CRYPTO_AES_ARM64_CE is not set\n# CONFIG_CRYPTO_AES_ARM64_CE_BLK is not set\n# CONFIG_CRYPTO_AES_ARM64_NEON_BLK is not set\n# CONFIG_CRYPTO_AES_ARM64_BS is not set\n# CONFIG_CRYPTO_SM4_ARM64_CE is not set\n# CONFIG_CRYPTO_SM4_ARM64_CE_BLK is not set\n# CONFIG_CRYPTO_SM4_ARM64_NEON_BLK is not set\n# CONFIG_CRYPTO_AES_ARM64_CE_CCM is not set\n# CONFIG_CRYPTO_CRCT10DIF_ARM64_CE is not set\n# end of Accelerated Cryptographic Algorithms for CPU (arm64)\n\n# CONFIG_CRYPTO_HW is not set\n# CONFIG_ASYMMETRIC_KEY_TYPE is not set\n\n#\n# Certificates for signature checking\n#\nCONFIG_SYSTEM_BLACKLIST_KEYRING=y\nCONFIG_SYSTEM_BLACKLIST_HASH_LIST=\"\"\n# end of Certificates for signature checking\n\nCONFIG_BINARY_PRINTF=y\n\n#\n# Library routines\n#\n# CONFIG_PACKING is not set\nCONFIG_BITREVERSE=y\nCONFIG_HAVE_ARCH_BITREVERSE=y\nCONFIG_GENERIC_STRNCPY_FROM_USER=y\nCONFIG_GENERIC_STRNLEN_USER=y\nCONFIG_GENERIC_NET_UTILS=y\n# CONFIG_CORDIC is not set\n# CONFIG_PRIME_NUMBERS is not set\nCONFIG_RATIONAL=y\nCONFIG_GENERIC_PCI_IOMAP=y\nCONFIG_ARCH_USE_CMPXCHG_LOCKREF=y\nCONFIG_ARCH_HAS_FAST_MULTIPLIER=y\nCONFIG_ARCH_USE_SYM_ANNOTATIONS=y\n# CONFIG_INDIRECT_PIO is not set\n\n#\n# Crypto library routines\n#\nCONFIG_CRYPTO_LIB_UTILS=y\nCONFIG_CRYPTO_LIB_AES=y\nCONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y\nCONFIG_CRYPTO_ARCH_HAVE_LIB_CHACHA=y\nCONFIG_CRYPTO_LIB_CHACHA_GENERIC=y\nCONFIG_CRYPTO_LIB_CHACHA=y\nCONFIG_CRYPTO_LIB_CURVE25519_GENERIC=y\nCONFIG_CRYPTO_LIB_CURVE25519=y\nCONFIG_CRYPTO_LIB_DES=y\nCONFIG_CRYPTO_LIB_POLY1305_RSIZE=9\nCONFIG_CRYPTO_ARCH_HAVE_LIB_POLY1305=y\nCONFIG_CRYPTO_LIB_POLY1305_GENERIC=y\nCONFIG_CRYPTO_LIB_POLY1305=y\nCONFIG_CRYPTO_LIB_CHACHA20POLY1305=y\nCONFIG_CRYPTO_LIB_SHA1=y\nCONFIG_CRYPTO_LIB_SHA256=y\n# end of Crypto library routines\n\nCONFIG_CRC_CCITT=y\nCONFIG_CRC16=y\nCONFIG_CRC_T10DIF=y\n# CONFIG_CRC64_ROCKSOFT is not set\nCONFIG_CRC_ITU_T=y\nCONFIG_CRC32=y\n# CONFIG_CRC32_SELFTEST is not set\nCONFIG_CRC32_SLICEBY8=y\n# CONFIG_CRC32_SLICEBY4 is not set\n# CONFIG_CRC32_SARWATE is not set\n# CONFIG_CRC32_BIT is not set\n# CONFIG_CRC64 is not set\n# CONFIG_CRC4 is not set\n# CONFIG_CRC7 is not set\nCONFIG_LIBCRC32C=y\n# CONFIG_CRC8 is not set\nCONFIG_XXHASH=y\nCONFIG_AUDIT_GENERIC=y\nCONFIG_AUDIT_ARCH_COMPAT_GENERIC=y\n# CONFIG_RANDOM32_SELFTEST is not set\nCONFIG_ZLIB_INFLATE=y\nCONFIG_ZLIB_DEFLATE=y\nCONFIG_LZO_COMPRESS=y\nCONFIG_LZO_DECOMPRESS=y\nCONFIG_LZ4_DECOMPRESS=y\nCONFIG_XZ_DEC=y\nCONFIG_XZ_DEC_X86=y\nCONFIG_XZ_DEC_POWERPC=y\nCONFIG_XZ_DEC_IA64=y\nCONFIG_XZ_DEC_ARM=y\nCONFIG_XZ_DEC_ARMTHUMB=y\nCONFIG_XZ_DEC_SPARC=y\n# CONFIG_XZ_DEC_MICROLZMA is not set\nCONFIG_XZ_DEC_BCJ=y\n# CONFIG_XZ_DEC_TEST is not set\nCONFIG_DECOMPRESS_GZIP=y\nCONFIG_DECOMPRESS_BZIP2=y\nCONFIG_DECOMPRESS_LZMA=y\nCONFIG_DECOMPRESS_XZ=y\nCONFIG_DECOMPRESS_LZO=y\nCONFIG_DECOMPRESS_LZ4=y\nCONFIG_GENERIC_ALLOCATOR=y\nCONFIG_TEXTSEARCH=y\nCONFIG_TEXTSEARCH_KMP=y\nCONFIG_TEXTSEARCH_BM=y\nCONFIG_TEXTSEARCH_FSM=y\nCONFIG_INTERVAL_TREE=y\nCONFIG_XARRAY_MULTI=y\nCONFIG_ASSOCIATIVE_ARRAY=y\nCONFIG_HAS_IOMEM=y\nCONFIG_HAS_IOPORT_MAP=y\nCONFIG_HAS_DMA=y\nCONFIG_DMA_OPS=y\nCONFIG_NEED_SG_DMA_LENGTH=y\nCONFIG_NEED_DMA_MAP_STATE=y\nCONFIG_ARCH_DMA_ADDR_T_64BIT=y\nCONFIG_DMA_DECLARE_COHERENT=y\nCONFIG_ARCH_HAS_SETUP_DMA_OPS=y\nCONFIG_ARCH_HAS_TEARDOWN_DMA_OPS=y\nCONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE=y\nCONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU=y\nCONFIG_ARCH_HAS_DMA_PREP_COHERENT=y\nCONFIG_SWIOTLB=y\n# CONFIG_DMA_RESTRICTED_POOL is not set\nCONFIG_DMA_NONCOHERENT_MMAP=y\nCONFIG_DMA_COHERENT_POOL=y\nCONFIG_DMA_DIRECT_REMAP=y\n# CONFIG_DMA_API_DEBUG is not set\n# CONFIG_DMA_MAP_BENCHMARK is not set\nCONFIG_SGL_ALLOC=y\n# CONFIG_FORCE_NR_CPUS is not set\nCONFIG_CPU_RMAP=y\nCONFIG_DQL=y\nCONFIG_NLATTR=y\nCONFIG_IRQ_POLL=y\nCONFIG_LIBFDT=y\nCONFIG_OID_REGISTRY=y\nCONFIG_UCS2_STRING=y\nCONFIG_HAVE_GENERIC_VDSO=y\nCONFIG_GENERIC_GETTIMEOFDAY=y\nCONFIG_GENERIC_VDSO_TIME_NS=y\nCONFIG_FONT_SUPPORT=y\n# CONFIG_FONTS is not set\nCONFIG_FONT_8x8=y\nCONFIG_FONT_8x16=y\nCONFIG_SG_POOL=y\nCONFIG_MEMREGION=y\nCONFIG_ARCH_STACKWALK=y\nCONFIG_STACKDEPOT=y\nCONFIG_SBITMAP=y\n# end of Library routines\n\nCONFIG_GENERIC_IOREMAP=y\nCONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y\n\n#\n# Kernel hacking\n#\n\n#\n# printk and dmesg options\n#\nCONFIG_PRINTK_TIME=y\n# CONFIG_PRINTK_CALLER is not set\n# CONFIG_STACKTRACE_BUILD_ID is not set\nCONFIG_CONSOLE_LOGLEVEL_DEFAULT=7\nCONFIG_CONSOLE_LOGLEVEL_QUIET=4\nCONFIG_MESSAGE_LOGLEVEL_DEFAULT=4\n# CONFIG_BOOT_PRINTK_DELAY is not set\nCONFIG_DYNAMIC_DEBUG=y\nCONFIG_DYNAMIC_DEBUG_CORE=y\nCONFIG_SYMBOLIC_ERRNAME=y\n# CONFIG_DEBUG_BUGVERBOSE is not set\n# end of printk and dmesg options\n\nCONFIG_DEBUG_KERNEL=y\nCONFIG_DEBUG_MISC=y\n\n#\n# Compile-time checks and compiler options\n#\nCONFIG_AS_HAS_NON_CONST_LEB128=y\nCONFIG_DEBUG_INFO_NONE=y\n# CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT is not set\n# CONFIG_DEBUG_INFO_DWARF4 is not set\n# CONFIG_DEBUG_INFO_DWARF5 is not set\nCONFIG_FRAME_WARN=2048\nCONFIG_STRIP_ASM_SYMS=y\n# CONFIG_READABLE_ASM is not set\n# CONFIG_HEADERS_INSTALL is not set\nCONFIG_DEBUG_SECTION_MISMATCH=y\nCONFIG_SECTION_MISMATCH_WARN_ONLY=y\n# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B is not set\nCONFIG_ARCH_WANT_FRAME_POINTERS=y\nCONFIG_FRAME_POINTER=y\nCONFIG_VMLINUX_MAP=y\n# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set\n# end of Compile-time checks and compiler options\n\n#\n# Generic Kernel Debugging Instruments\n#\nCONFIG_MAGIC_SYSRQ=y\nCONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1\nCONFIG_MAGIC_SYSRQ_SERIAL=y\nCONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE=\"\"\nCONFIG_DEBUG_FS=y\nCONFIG_DEBUG_FS_ALLOW_ALL=y\n# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set\n# CONFIG_DEBUG_FS_ALLOW_NONE is not set\nCONFIG_HAVE_ARCH_KGDB=y\n# CONFIG_KGDB is not set\nCONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y\n# CONFIG_UBSAN is not set\nCONFIG_HAVE_ARCH_KCSAN=y\n# end of Generic Kernel Debugging Instruments\n\n#\n# Networking Debugging\n#\n# CONFIG_NET_DEV_REFCNT_TRACKER is not set\n# CONFIG_NET_NS_REFCNT_TRACKER is not set\n# CONFIG_DEBUG_NET is not set\n# end of Networking Debugging\n\n#\n# Memory Debugging\n#\n# CONFIG_PAGE_EXTENSION is not set\n# CONFIG_DEBUG_PAGEALLOC is not set\nCONFIG_SLUB_DEBUG=y\n# CONFIG_SLUB_DEBUG_ON is not set\n# CONFIG_PAGE_OWNER is not set\n# CONFIG_PAGE_TABLE_CHECK is not set\n# CONFIG_PAGE_POISONING is not set\n# CONFIG_DEBUG_RODATA_TEST is not set\nCONFIG_ARCH_HAS_DEBUG_WX=y\n# CONFIG_DEBUG_WX is not set\nCONFIG_GENERIC_PTDUMP=y\n# CONFIG_PTDUMP_DEBUGFS is not set\n# CONFIG_DEBUG_OBJECTS is not set\n# CONFIG_SHRINKER_DEBUG is not set\nCONFIG_HAVE_DEBUG_KMEMLEAK=y\n# CONFIG_DEBUG_KMEMLEAK is not set\n# CONFIG_DEBUG_STACK_USAGE is not set\n# CONFIG_SCHED_STACK_END_CHECK is not set\nCONFIG_ARCH_HAS_DEBUG_VM_PGTABLE=y\n# CONFIG_DEBUG_VM is not set\n# CONFIG_DEBUG_VM_PGTABLE is not set\nCONFIG_ARCH_HAS_DEBUG_VIRTUAL=y\n# CONFIG_DEBUG_VIRTUAL is not set\nCONFIG_DEBUG_MEMORY_INIT=y\n# CONFIG_DEBUG_PER_CPU_MAPS is not set\nCONFIG_HAVE_ARCH_KASAN=y\nCONFIG_HAVE_ARCH_KASAN_SW_TAGS=y\nCONFIG_HAVE_ARCH_KASAN_HW_TAGS=y\nCONFIG_HAVE_ARCH_KASAN_VMALLOC=y\nCONFIG_CC_HAS_KASAN_GENERIC=y\nCONFIG_CC_HAS_WORKING_NOSANITIZE_ADDRESS=y\n# CONFIG_KASAN is not set\nCONFIG_HAVE_ARCH_KFENCE=y\n# CONFIG_KFENCE is not set\n# end of Memory Debugging\n\n# CONFIG_DEBUG_SHIRQ is not set\n\n#\n# Debug Oops, Lockups and Hangs\n#\n# CONFIG_PANIC_ON_OOPS is not set\nCONFIG_PANIC_ON_OOPS_VALUE=0\nCONFIG_PANIC_TIMEOUT=0\n# CONFIG_SOFTLOCKUP_DETECTOR is not set\n# CONFIG_DETECT_HUNG_TASK is not set\n# CONFIG_WQ_WATCHDOG is not set\n# end of Debug Oops, Lockups and Hangs\n\n#\n# Scheduler Debugging\n#\n# CONFIG_SCHED_DEBUG is not set\nCONFIG_SCHED_INFO=y\n# CONFIG_SCHEDSTATS is not set\n# end of Scheduler Debugging\n\n# CONFIG_DEBUG_TIMEKEEPING is not set\n\n#\n# Lock Debugging (spinlocks, mutexes, etc...)\n#\nCONFIG_LOCK_DEBUGGING_SUPPORT=y\n# CONFIG_PROVE_LOCKING is not set\n# CONFIG_LOCK_STAT is not set\n# CONFIG_DEBUG_RT_MUTEXES is not set\n# CONFIG_DEBUG_SPINLOCK is not set\n# CONFIG_DEBUG_MUTEXES is not set\n# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set\n# CONFIG_DEBUG_RWSEMS is not set\n# CONFIG_DEBUG_LOCK_ALLOC is not set\n# CONFIG_DEBUG_ATOMIC_SLEEP is not set\n# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set\n# CONFIG_LOCK_TORTURE_TEST is not set\n# CONFIG_WW_MUTEX_SELFTEST is not set\n# CONFIG_SCF_TORTURE_TEST is not set\n# CONFIG_CSD_LOCK_WAIT_DEBUG is not set\n# end of Lock Debugging (spinlocks, mutexes, etc...)\n\n# CONFIG_DEBUG_IRQFLAGS is not set\nCONFIG_STACKTRACE=y\n# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set\n# CONFIG_DEBUG_KOBJECT is not set\n\n#\n# Debug kernel data structures\n#\nCONFIG_DEBUG_LIST=y\n# CONFIG_DEBUG_PLIST is not set\n# CONFIG_DEBUG_SG is not set\n# CONFIG_DEBUG_NOTIFIERS is not set\nCONFIG_BUG_ON_DATA_CORRUPTION=y\n# CONFIG_DEBUG_MAPLE_TREE is not set\n# end of Debug kernel data structures\n\n# CONFIG_DEBUG_CREDENTIALS is not set\n\n#\n# RCU Debugging\n#\n# CONFIG_RCU_SCALE_TEST is not set\n# CONFIG_RCU_TORTURE_TEST is not set\n# CONFIG_RCU_REF_SCALE_TEST is not set\nCONFIG_RCU_CPU_STALL_TIMEOUT=59\nCONFIG_RCU_EXP_CPU_STALL_TIMEOUT=0\n# CONFIG_RCU_TRACE is not set\n# CONFIG_RCU_EQS_DEBUG is not set\n# end of RCU Debugging\n\n# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set\n# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set\n# CONFIG_LATENCYTOP is not set\nCONFIG_HAVE_FUNCTION_TRACER=y\nCONFIG_HAVE_FUNCTION_GRAPH_TRACER=y\nCONFIG_HAVE_DYNAMIC_FTRACE=y\nCONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y\nCONFIG_HAVE_FTRACE_MCOUNT_RECORD=y\nCONFIG_HAVE_SYSCALL_TRACEPOINTS=y\nCONFIG_HAVE_C_RECORDMCOUNT=y\nCONFIG_TRACING_SUPPORT=y\n# CONFIG_FTRACE is not set\n# CONFIG_SAMPLES is not set\nCONFIG_STRICT_DEVMEM=y\n# CONFIG_IO_STRICT_DEVMEM is not set\n\n#\n# arm64 Debugging\n#\n# CONFIG_PID_IN_CONTEXTIDR is not set\n# CONFIG_CORESIGHT is not set\n# end of arm64 Debugging\n\n#\n# Kernel Testing and Coverage\n#\n# CONFIG_KUNIT is not set\n# CONFIG_NOTIFIER_ERROR_INJECTION is not set\n# CONFIG_FAULT_INJECTION is not set\nCONFIG_ARCH_HAS_KCOV=y\nCONFIG_CC_HAS_SANCOV_TRACE_PC=y\nCONFIG_RUNTIME_TESTING_MENU=y\n# CONFIG_LKDTM is not set\n# CONFIG_TEST_MIN_HEAP is not set\n# CONFIG_TEST_DIV64 is not set\n# CONFIG_BACKTRACE_SELF_TEST is not set\n# CONFIG_TEST_REF_TRACKER is not set\n# CONFIG_RBTREE_TEST is not set\n# CONFIG_REED_SOLOMON_TEST is not set\n# CONFIG_INTERVAL_TREE_TEST is not set\n# CONFIG_ATOMIC64_SELFTEST is not set\n# CONFIG_TEST_HEXDUMP is not set\n# CONFIG_STRING_SELFTEST is not set\n# CONFIG_TEST_STRING_HELPERS is not set\n# CONFIG_TEST_STRSCPY is not set\n# CONFIG_TEST_KSTRTOX is not set\n# CONFIG_TEST_PRINTF is not set\n# CONFIG_TEST_SCANF is not set\n# CONFIG_TEST_BITMAP is not set\n# CONFIG_TEST_UUID is not set\n# CONFIG_TEST_XARRAY is not set\n# CONFIG_TEST_MAPLE_TREE is not set\n# CONFIG_TEST_RHASHTABLE is not set\n# CONFIG_TEST_SIPHASH is not set\n# CONFIG_TEST_IDA is not set\n# CONFIG_FIND_BIT_BENCHMARK is not set\n# CONFIG_TEST_FIRMWARE is not set\n# CONFIG_TEST_SYSCTL is not set\n# CONFIG_TEST_UDELAY is not set\n# CONFIG_TEST_DYNAMIC_DEBUG is not set\n# CONFIG_TEST_MEMCAT_P is not set\n# CONFIG_TEST_MEMINIT is not set\n# CONFIG_TEST_FREE_PAGES is not set\nCONFIG_ARCH_USE_MEMTEST=y\n# CONFIG_MEMTEST is not set\n# end of Kernel Testing and Coverage\n\n#\n# Rust hacking\n#\n# end of Rust hacking\n# end of Kernel hacking\n"
  },
  {
    "path": "kernel/image/Dockerfile",
    "content": "FROM ubuntu:focal\n\nRUN apt-get update && apt-get install -y \\\n    autoconf \\\n    bc \\\n    binutils-multiarch \\\n    binutils-aarch64-linux-gnu \\\n    bison \\\n    flex \\\n    gcc \\\n    xz-utils \\\n    gcc-aarch64-linux-gnu \\\n    git \\\n    libncurses-dev \\\n    make \\\n    openssl \\\n    python-is-python3 \\\n&&  apt-get clean \\\n&&  rm -rf /var/lib/apt/lists/*\n\nCOPY sources.list /etc/apt/sources.list\n\nRUN apt-get update \\\n&&  dpkg --add-architecture arm64 \\\n&&  apt-get install -y libelf-dev:arm64 \\\n&&  apt-get clean \\\n&&  rm -rf /var/lib/apt/lists/*"
  },
  {
    "path": "kernel/image/sources.list",
    "content": "deb [arch=arm64] http://ports.ubuntu.com/ focal main restricted\ndeb [arch=arm64] http://ports.ubuntu.com/ focal-updates main restricted\ndeb [arch=arm64] http://ports.ubuntu.com/ focal universe\ndeb [arch=arm64] http://ports.ubuntu.com/ focal-updates universe\ndeb [arch=arm64] http://ports.ubuntu.com/ focal multiverse\ndeb [arch=arm64] http://ports.ubuntu.com/ focal-updates multiverse\ndeb [arch=arm64] http://ports.ubuntu.com/ focal-backports main restricted universe multiverse\n\ndeb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal main\ndeb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal-updates main restricted\ndeb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal universe\ndeb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal-updates universe\ndeb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal multiverse\ndeb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal-updates multiverse\ndeb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal-backports main restricted universe multiverse"
  },
  {
    "path": "licenserc.toml",
    "content": "additionalHeaders = [\"scripts/cz-header-style.toml\"]\n\nheaderPath = \"scripts/license-header.txt\"\n\nincludes = [\n    \"Makefile\",\n    \"*.Makefile\",\n    \"*.swift\",\n    \"*.h\",\n    \"*.cpp\",\n    \"*.c\",\n    \"*.sh\",\n]\n\nexcludes = [\n    \"Sources/ContainerizationArchive/CArchive/include\",\n]\n\n[git]\nattrs = 'enable'\nignore = 'enable'\n\n[properties]\ncopyrightOwner = \"Apple Inc. and the Containerization project authors\"\n\n[mapping.SWIFT_STYLE]\nextensions = [\"swift\"]\n"
  },
  {
    "path": "scripts/check-integration-test-vm-panics.sh",
    "content": "#!/bin/bash\n# Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#   https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# Script to scan the VM boot logs from the integration tests for kernel panics.\n# Looks for common kernel panic messages like \"attempted to kill init\" or \"Kernel panic\".\n\nGIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)\nif [ -z \"$GIT_ROOT\" ]; then\n    echo \"Error: Not in a git repository\"\n    exit 1\nfi\n\nBOOT_LOGS_DIR=\"$GIT_ROOT/bin/integration-bootlogs\"\n\nif [ ! -d \"$BOOT_LOGS_DIR\" ]; then\n    echo \"Error: Boot logs directory not found: $BOOT_LOGS_DIR\"\n    exit 1\nfi\n\necho \"Scanning boot logs in: $BOOT_LOGS_DIR\"\necho \"========================================\"\necho \"\"\n\nPANIC_FOUND=0\n\nfor logfile in \"$BOOT_LOGS_DIR\"/*; do\n    if [ -f \"$logfile\" ]; then\n        if grep -qi \"attempted to kill init\\|Kernel panic\\|end Kernel panic\\|Attempted to kill the idle task\\|Oops:\" \"$logfile\"; then\n            echo \"🚨 PANIC DETECTED in: $(basename \"$logfile\")\"\n            echo \"---\"\n            grep -i -B 5 -A 10 \"attempted to kill init\\|Kernel panic\\|end Kernel panic\\|Attempted to kill the idle task\\|Oops:\" \"$logfile\" | head -30\n            echo \"\"\n            echo \"========================================\"\n            echo \"\"\n            PANIC_FOUND=1\n        fi\n    fi\ndone\n\nif [ $PANIC_FOUND -eq 0 ]; then\n    echo \"✅ No kernel panics detected in boot logs\"\nelse\n    echo \"❌ Found kernel panics - Virtual machine(s) crashed during integration tests\"\nfi\n\nexit $PANIC_FOUND\n"
  },
  {
    "path": "scripts/cz-header-style.toml",
    "content": "[SWIFT_STYLE]\nfirstLine = '//===----------------------------------------------------------------------===//'\nendLine = \"//===----------------------------------------------------------------------===//\\n\"\nbeforeEachLine = '// '\nafterEachLine = ''\nallowBlankLines = false\nmultipleLines = true\npadLines = false\nfirstLineDetectionPattern = '//\\s?==='\nlastLineDetectionPattern = '//\\s?==='\nskipLinePattern = '// swift-tools-version'\n"
  },
  {
    "path": "scripts/ensure-hawkeye-exists.sh",
    "content": "#!/usr/bin/env bash\n# Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#   https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\necho \"Checking existence of hawkeye...\"\n\nif command -v .local/bin/hawkeye >/dev/null 2>&1; then\n    echo \"hawkeye found!\"\nelse\n    echo \"hawkeye not found in PATH\"\n    echo \"please install hawkeye. For convenience, you can run scripts/install-hawkeye.sh\"\n    exit 1\nfi\n"
  },
  {
    "path": "scripts/install-hawkeye.sh",
    "content": "#!/usr/bin/env bash \n# Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#   https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nif command -v .local/bin/hawkeye >/dev/null 2>&1; then\n    echo \"hawkeye already installed\"\nelse\n    echo \"Installing hawkeye\"\n    export VERSION=v6.1.0\n    curl --proto '=https' --tlsv1.2 -LsSf https://github.com/korandoru/hawkeye/releases/download/${VERSION}/hawkeye-installer.sh | CARGO_HOME=.local sh -s -- --no-modify-path\nfi\n"
  },
  {
    "path": "scripts/license-header.txt",
    "content": "Copyright ©{{ \" \" }}{%- set created = attrs.git_file_created_year or attrs.disk_file_created_year -%}{%- set modified = attrs.git_file_modified_year or created -%}{%- if created != modified -%} {{created}}-{{modified}}{%- else -%}{{created}}{%- endif -%}{{ \" \" }}{{ props[\"copyrightOwner\"] }}.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "scripts/make-docs.sh",
    "content": "#! /bin/bash -e\n# Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#   https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nopts=()\nopts+=(\"--allow-writing-to-directory\" \"$1\")\nopts+=(\"generate-documentation\")\nopts+=(\"--target\" \"Containerization\")\nopts+=(\"--target\" \"ContainerizationArchive\")\nopts+=(\"--target\" \"ContainerizationError\")\nopts+=(\"--target\" \"ContainerizationEXT4\")\nopts+=(\"--target\" \"ContainerizationExtras\")\nopts+=(\"--target\" \"ContainerizationIO\")\nopts+=(\"--target\" \"ContainerizationNetlink\")\nopts+=(\"--target\" \"ContainerizationOCI\")\nopts+=(\"--target\" \"ContainerizationOS\")\nopts+=(\"--output-path\" \"$1\")\nopts+=(\"--disable-indexing\")\nopts+=(\"--transform-for-static-hosting\")\nopts+=(\"--enable-experimental-combined-documentation\")\nopts+=(\"--experimental-documentation-coverage\")\n\nif [ ! -z \"$2\" ] ; then\n    opts+=(\"--hosting-base-path\" \"$2\")\nfi\n\n/usr/bin/swift package ${opts[@]}\n\necho '{}' > \"$1/theme-settings.json\"\n\ncat > \"$1/index.html\" <<'EOF'\n<html lang=\"en-US\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"refresh\" content=\"0; url=./documentation/\">\n  </head>\n  <body>\n    <p>If you are not redirected automatically, <a href=\"./documentation/\">click here</a>.</p>\n  </body>\n</html>\nEOF\n"
  },
  {
    "path": "scripts/pre-commit.fmt",
    "content": "#! /bin/bash -e\n\nsetup_error() {\n    echo failed to run: $1 1>&2\n    echo run '\"make pre-commit\"' and try again 1>&2\n    exit 1\n}\n\nif [ ! -z \"${PRECOMMIT_NOFMT}\" ] ; then\n    exit 0\nfi\n\necho checking formatting and licenses 1>&2\nproject_pathname=$(git rev-parse --show-toplevel)\ncd \"${project_pathname}\"\nmake check\n"
  },
  {
    "path": "signing/vz.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.virtualization</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "vminitd/.devcontainer/Dockerfile",
    "content": "FROM swift:6.2\n\nRUN apt-get update \\\n&&  apt-get install make \\\n&&  apt-get clean \\\n&&  rm -rf /var/lib/apt/lists/*\nRUN swift sdk install https://download.swift.org/swift-6.2.3-release/static-sdk/swift-6.2.3-RELEASE/swift-6.2.3-RELEASE_static-linux-0.0.1.artifactbundle.tar.gz --checksum f30ec724d824ef43b5546e02ca06a8682dafab4b26a99fbb0e858c347e507a2c\n"
  },
  {
    "path": "vminitd/.devcontainer/devcontainer.json",
    "content": "{\n    \"build\": {\n        \"dockerfile\": \"Dockerfile\"\n    },\n    \"features\": {},\n    \"customizations\": {\n        \"vscode\": {\n            \"extensions\": [\n                \"swiftlang.swift-vscode\"\n            ],\n            \"settings\": {\n            }\n        }\n    },\n    \"runArgs\": [],\n    \"mounts\": []\n}\n"
  },
  {
    "path": "vminitd/Makefile",
    "content": "# Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#   https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nBUILD_CONFIGURATION ?= debug\nWARNINGS_AS_ERRORS ?= true\nSWIFT_WARNING_CONFIG := $(if $(filter-out false,$(WARNINGS_AS_ERRORS)),-Xswiftc -warnings-as-errors)\nSWIFT_CONFIGURATION := --swift-sdk aarch64-swift-linux-musl $(SWIFT_WARNING_CONFIG) -Xlinker -s --disable-automatic-resolution\n\nSWIFT_VERSION := 6.3-snapshot-2026-02-27\nSWIFT_SDK_URL := https://download.swift.org/swift-6.3-branch/static-sdk/swift-6.3-DEVELOPMENT-SNAPSHOT-2026-02-27-a/swift-6.3-DEVELOPMENT-SNAPSHOT-2026-02-27-a_static-linux-0.1.0.artifactbundle.tar.gz\nSWIFT_SDK_PATH := /tmp/$(notdir $(SWIFT_SDK_URL))\n\nSWIFTLY_URL := https://download.swift.org/swiftly/darwin/swiftly.pkg\nSWIFTLY_FILENAME := $(notdir $(SWIFTLY_URL))\nSWIFTLY_BIN_DIR ?= ~/.swiftly/bin\nBUILD_BIN_DIR := $(shell swift build -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --show-bin-path)\n\nSYSTEM_TYPE := $(shell uname -s)\nifeq ($(SYSTEM_TYPE),Darwin)\nMACOS_VERSION := $(shell sw_vers -productVersion)\nMACOS_MAJOR := $(shell echo $(MACOS_VERSION) | cut -d. -f1)\nMACOS_RELEASE_TYPE := $(shell sw_vers | grep ReleaseType)\nendif\n\n.DEFAULT_GOAL := all\n\n.PHONY: all\nall: \n\t@echo Building vminitd and vmexec...\n\t@mkdir -p ./bin/\n\t@rm -f ./bin/vminitd\n\t@rm -f ./bin/vmexec\n\t@swift --version\n\t@swift build -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION)\n\t@install \"$(BUILD_BIN_DIR)/vminitd\" ./bin/\n\t@install \"$(BUILD_BIN_DIR)/vmexec\" ./bin/\n\n.PHONY: cross-prep\ncross-prep: linux-sdk macos-sdk\n\n.PHONY: swiftly\nswiftly:\n\t@if ! command -v ${SWIFTLY_BIN_DIR}/swiftly > /dev/null 2>&1; then \\\n\t\techo \"Installing Swiftly...\"; \\\n\t\tcurl -o /var/tmp/$(SWIFTLY_FILENAME) $(SWIFTLY_URL) && \\\n\t\tinstaller -pkg /var/tmp/$(SWIFTLY_FILENAME) -target CurrentUserHomeDirectory && \\\n\t\t${SWIFTLY_BIN_DIR}/swiftly init --quiet-shell-followup --skip-install && \\\n\t\t. ~/.swiftly/env.sh && \\\n\t\thash -r && \\\n\t\trm /var/tmp/$(SWIFTLY_FILENAME); \\\n\tfi\n\t\n.PHONY: swift\nswift: swiftly\n\t@echo Installing Swift $(SWIFT_VERSION)...\n\t@${SWIFTLY_BIN_DIR}/swiftly install $(SWIFT_VERSION)\n\n.PHONY: linux-sdk\nlinux-sdk: swift\n\t@echo Installing Static Linux SDK...\n\t@curl -L -o $(SWIFT_SDK_PATH) $(SWIFT_SDK_URL)\n\t-@swift sdk install $(SWIFT_SDK_PATH)\n\t@rm $(SWIFT_SDK_PATH)\n\n.PHONY: macos-sdk\nmacos-sdk:\n\t# Consider switching back to `xcode-cltools`, when possible.\n\t@if [ $(MACOS_MAJOR) -gt 15 ] && [ \"$(MACOS_RELEASE_TYPE)\" = \"\" ]; then \\\n\t  \"$(MAKE)\" xcode; \\\n\telse \\\n\t  \"$(MAKE)\" xcode; \\\n\tfi\n\n.PHONY: xcode-cltools\nxcode-cltools:\n\t@echo Activating Xcode Command Line Tools...\n\t@sudo xcode-select --switch /Library/Developer/CommandLineTools\n\n.PHONY: xcode\nxcode:\n\t@echo \"Please install the latest version of Xcode 26 and set the path for the active developer directory using \\`sudo xcode-select -s <PATH_TO_XCODE>\\`\".\n\n.PHONY: clean\nclean:\n\t@echo Cleaning the vminitd build files...\n\t@rm -f ./bin/vminitd\n\t@rm -f ./bin/vmexec\n\t@swift package clean $(SWIFT_CONFIGURATION)\n"
  },
  {
    "path": "vminitd/Package.resolved",
    "content": "{\n  \"originHash\" : \"db3e9fbb73707e38ad14f86a67eb82b8c1a92edeb98b8c6747530a33f8d87125\",\n  \"pins\" : [\n    {\n      \"identity\" : \"async-http-client\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swift-server/async-http-client.git\",\n      \"state\" : {\n        \"revision\" : \"60235983163d040f343a489f7e2e77c1918a8bd9\",\n        \"version\" : \"1.26.1\"\n      }\n    },\n    {\n      \"identity\" : \"grpc-swift\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/grpc/grpc-swift.git\",\n      \"state\" : {\n        \"revision\" : \"a56a157218877ef3e9625f7e1f7b2cb7e46ead1b\",\n        \"version\" : \"1.26.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-algorithms\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-algorithms.git\",\n      \"state\" : {\n        \"revision\" : \"87e50f483c54e6efd60e885f7f5aa946cee68023\",\n        \"version\" : \"1.2.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-argument-parser\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-argument-parser.git\",\n      \"state\" : {\n        \"revision\" : \"011f0c765fb46d9cac61bca19be0527e99c98c8b\",\n        \"version\" : \"1.5.1\"\n      }\n    },\n    {\n      \"identity\" : \"swift-asn1\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-asn1.git\",\n      \"state\" : {\n        \"revision\" : \"a54383ada6cecde007d374f58f864e29370ba5c3\",\n        \"version\" : \"1.3.2\"\n      }\n    },\n    {\n      \"identity\" : \"swift-async-algorithms\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-async-algorithms.git\",\n      \"state\" : {\n        \"revision\" : \"042e1c4d9d19748c9c228f8d4ebc97bb1e339b0b\",\n        \"version\" : \"1.0.4\"\n      }\n    },\n    {\n      \"identity\" : \"swift-atomics\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-atomics.git\",\n      \"state\" : {\n        \"revision\" : \"cd142fd2f64be2100422d658e7411e39489da985\",\n        \"version\" : \"1.2.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-certificates\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-certificates.git\",\n      \"state\" : {\n        \"revision\" : \"999fd70c7803da89f3904d635a6815a2a7cd7585\",\n        \"version\" : \"1.10.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-collections\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-collections.git\",\n      \"state\" : {\n        \"revision\" : \"c1805596154bb3a265fd91b8ac0c4433b4348fb0\",\n        \"version\" : \"1.2.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-crypto\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-crypto.git\",\n      \"state\" : {\n        \"revision\" : \"e8d6eba1fef23ae5b359c46b03f7d94be2f41fed\",\n        \"version\" : \"3.12.3\"\n      }\n    },\n    {\n      \"identity\" : \"swift-http-structured-headers\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-http-structured-headers.git\",\n      \"state\" : {\n        \"revision\" : \"db6eea3692638a65e2124990155cd220c2915903\",\n        \"version\" : \"1.3.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-http-types\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-http-types.git\",\n      \"state\" : {\n        \"revision\" : \"a0a57e949a8903563aba4615869310c0ebf14c03\",\n        \"version\" : \"1.4.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-log\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-log.git\",\n      \"state\" : {\n        \"revision\" : \"3d8596ed08bd13520157f0355e35caed215ffbfa\",\n        \"version\" : \"1.6.3\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio.git\",\n      \"state\" : {\n        \"revision\" : \"34d486b01cd891297ac615e40d5999536a1e138d\",\n        \"version\" : \"2.83.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-extras\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-extras.git\",\n      \"state\" : {\n        \"revision\" : \"145db1962f4f33a4ea07a32e751d5217602eea29\",\n        \"version\" : \"1.28.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-http2\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-http2.git\",\n      \"state\" : {\n        \"revision\" : \"4281466512f63d1bd530e33f4aa6993ee7864be0\",\n        \"version\" : \"1.36.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-ssl\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-ssl.git\",\n      \"state\" : {\n        \"revision\" : \"173cc69a058623525a58ae6710e2f5727c663793\",\n        \"version\" : \"2.36.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-nio-transport-services\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-nio-transport-services.git\",\n      \"state\" : {\n        \"revision\" : \"cd1e89816d345d2523b11c55654570acd5cd4c56\",\n        \"version\" : \"1.24.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-numerics\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-numerics.git\",\n      \"state\" : {\n        \"revision\" : \"e0ec0f5f3af6f3e4d5e7a19d2af26b481acb6ba8\",\n        \"version\" : \"1.0.3\"\n      }\n    },\n    {\n      \"identity\" : \"swift-protobuf\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-protobuf.git\",\n      \"state\" : {\n        \"revision\" : \"102a647b573f60f73afdce5613a51d71349fe507\",\n        \"version\" : \"1.30.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-service-lifecycle\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/swift-server/swift-service-lifecycle.git\",\n      \"state\" : {\n        \"revision\" : \"e7187309187695115033536e8fc9b2eb87fd956d\",\n        \"version\" : \"2.8.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-system\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-system.git\",\n      \"state\" : {\n        \"revision\" : \"395a77f0aa927f0ff73941d7ac35f2b46d47c9db\",\n        \"version\" : \"1.6.3\"\n      }\n    },\n    {\n      \"identity\" : \"zstd\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/facebook/zstd.git\",\n      \"state\" : {\n        \"revision\" : \"f8745da6ff1ad1e7bab384bd1f9d742439278e99\",\n        \"version\" : \"1.5.7\"\n      }\n    }\n  ],\n  \"version\" : 3\n}\n"
  },
  {
    "path": "vminitd/Package.swift",
    "content": "// swift-tools-version: 6.3\n//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n// The swift-tools-version declares the minimum version of Swift required to build this package.\n\nimport PackageDescription\n\nlet package = Package(\n    name: \"swift-vminitd\",\n    platforms: [.macOS(\"15\")],\n    products: [\n        .executable(name: \"vminitd\", targets: [\"vminitd\"]),\n        .executable(name: \"vmexec\", targets: [\"vmexec\"]),\n    ],\n    dependencies: [\n        .package(url: \"https://github.com/apple/swift-argument-parser\", from: \"1.3.0\"),\n        .package(url: \"https://github.com/apple/swift-log.git\", from: \"1.0.0\"),\n        .package(url: \"https://github.com/apple/swift-system.git\", from: \"1.6.3\"),\n        .package(name: \"containerization\", path: \"../\"),\n    ],\n    targets: [\n        .target(\n            name: \"LCShim\"\n        ),\n        .target(\n            name: \"Cgroup\",\n            dependencies: [\n                .product(name: \"Logging\", package: \"swift-log\"),\n                .product(name: \"ContainerizationOCI\", package: \"containerization\"),\n                .product(name: \"ContainerizationOS\", package: \"containerization\"),\n                .product(name: \"SystemPackage\", package: \"swift-system\"),\n            ]\n        ),\n        .executableTarget(\n            name: \"vminitd\",\n            dependencies: [\n                .product(name: \"ArgumentParser\", package: \"swift-argument-parser\"),\n                .product(name: \"Logging\", package: \"swift-log\"),\n                .product(name: \"Containerization\", package: \"containerization\"),\n                .product(name: \"ContainerizationArchive\", package: \"containerization\"),\n                .product(name: \"ContainerizationNetlink\", package: \"containerization\"),\n                .product(name: \"ContainerizationIO\", package: \"containerization\"),\n                .product(name: \"ContainerizationOS\", package: \"containerization\"),\n                .product(name: \"SystemPackage\", package: \"swift-system\"),\n                \"LCShim\",\n                \"Cgroup\",\n            ]\n        ),\n        .executableTarget(\n            name: \"vmexec\",\n            dependencies: [\n                .product(name: \"Logging\", package: \"swift-log\"),\n                .product(name: \"ArgumentParser\", package: \"swift-argument-parser\"),\n                .product(name: \"SystemPackage\", package: \"swift-system\"),\n                .product(name: \"Containerization\", package: \"containerization\"),\n                .product(name: \"ContainerizationOS\", package: \"containerization\"),\n                \"LCShim\",\n                \"Cgroup\",\n            ]\n        ),\n    ]\n)\n"
  },
  {
    "path": "vminitd/Sources/Cgroup/Cgroup2Manager.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n// NOTE: Ideally this should live in ContainerizationOS/Linux, or just ContainerizationCgroups\n// or something similar, but it's not there yet. It does what we need, but it'd need a lot more\n// features and testing before it's ready to be public.\n\n#if os(Linux)\n\n#if canImport(Musl)\nimport Musl\n#elseif canImport(Glibc)\nimport Glibc\n#endif\n\nimport ContainerizationOCI\nimport ContainerizationOS\nimport Foundation\nimport Logging\n\npackage enum Cgroup2Controller: String {\n    case pids\n    case memory\n    case cpuset\n    case cpu\n    case io\n    case hugetlb\n}\n\n// Extremely simple cgroup manager. Our needs are simple for now, and this is\n// reflected in the type.\npackage struct Cgroup2Manager: Sendable {\n    package static let defaultMountPoint = URL(filePath: \"/sys/fs/cgroup\")\n\n    private static let killFile = \"cgroup.kill\"\n    private static let procsFile = \"cgroup.procs\"\n    private static let subtreeControlFile = \"cgroup.subtree_control\"\n\n    private static let cg2Magic = 0x6367_7270\n\n    private let mountPoint: URL\n    private let path: URL\n    private let logger: Logger?\n\n    package init(\n        mountPoint: URL = Self.defaultMountPoint,\n        group: URL,\n        logger: Logger? = nil\n    ) {\n        self.mountPoint = mountPoint\n        self.path = mountPoint.appending(path: group.path)\n        self.logger = logger\n    }\n\n    package static func load(\n        mountPoint: URL = Self.defaultMountPoint,\n        group: URL,\n        logger: Logger? = nil\n    ) throws -> Cgroup2Manager {\n        let path = mountPoint.appending(path: group.path)\n        var s = statfs()\n        let res = statfs(path.path, &s)\n        if res != 0 {\n            throw Error.errno(errno: errno, message: \"failed to statfs \\(path.path)\")\n        }\n        if Int64(s.f_type) != Self.cg2Magic {\n            throw Error.notCgroup\n        }\n        return Cgroup2Manager(\n            mountPoint: mountPoint,\n            group: group,\n            logger: logger\n        )\n    }\n\n    package static func loadFromPid(pid: Int32, logger: Logger? = nil) throws -> Cgroup2Manager {\n        let procCgPath = URL(filePath: \"/proc/\\(pid)/cgroup\")\n        let fh = try FileHandle(forReadingFrom: procCgPath)\n        guard let data = try fh.readToEnd() else {\n            throw Error.errno(errno: errno, message: \"failed to read \\(procCgPath)\")\n        }\n\n        // If this fails we have bigger problems.\n        let str = String(data: data, encoding: .utf8)!\n        let parts = str.split(separator: \":\")\n        if parts[0] != \"0\" {\n            throw Error.cgroup1\n        }\n\n        // We should really read /proc/pid/mountinfo, but for now just assume\n        // it's always at /sys/fs/cgroup.\n        let path = parts[1].trimmingCharacters(in: .whitespacesAndNewlines)\n        return Cgroup2Manager(group: URL(filePath: String(path)), logger: logger)\n    }\n\n    package func create(perms: Int16 = 0o755) throws {\n        self.logger?.info(\n            \"creating cgroup manager\",\n            metadata: [\n                \"mountpoint\": \"\\(self.mountPoint.path)\",\n                \"path\": \"\\(self.path.path)\",\n            ])\n\n        try FileManager.default.createDirectory(\n            at: self.path,\n            withIntermediateDirectories: true,\n            attributes: [.posixPermissions: perms]\n        )\n    }\n\n    private static func writeValue(path: URL, value: String, fileName: String) throws {\n        let file = path.appending(path: fileName)\n        let fd = open(file.path, O_WRONLY, 0)\n        if fd == -1 {\n            throw Error.errno(errno: errno, message: \"failed to open \\(file.path)\")\n        }\n        defer { close(fd) }\n\n        let bytes = Array(value.utf8)\n        let res = Syscall.retrying {\n            bytes.withUnsafeBytes { write(fd, $0.baseAddress!, bytes.count) }\n        }\n        if res == -1 {\n            throw Error.errno(errno: errno, message: \"failed to write to \\(file.path)\")\n        }\n    }\n\n    package func toggleSubtreeControllers(controllers: [Cgroup2Controller], enable: Bool) throws {\n        let value = controllers.map { (enable ? \"+\" : \"-\") + $0.rawValue }.joined(separator: \" \")\n        let mountComponents = self.mountPoint.pathComponents\n        let pathComponents = self.path.pathComponents\n\n        // First ensure it's set on the root.\n        var current = self.mountPoint\n        try Self.writeValue(\n            path: current,\n            value: value,\n            fileName: Self.subtreeControlFile\n        )\n\n        // Toggle everything except the leaf, as otherwise we won't be able to write\n        // to cgroup.procs, and what fun is that :)\n        if mountComponents.count < pathComponents.count - 1 {\n            for i in mountComponents.count...pathComponents.count - 2 {\n                current = current.appending(path: pathComponents[i])\n                try Self.writeValue(\n                    path: current,\n                    value: value,\n                    fileName: Self.subtreeControlFile\n                )\n            }\n        }\n    }\n\n    package func toggleAllAvailableControllers(enable: Bool) throws {\n        // Read available controllers from cgroup.controllers\n        let controllersFile = self.mountPoint.appending(path: \"cgroup.controllers\")\n        let controllersContent = try String(contentsOf: controllersFile, encoding: .utf8)\n            .trimmingCharacters(in: .whitespacesAndNewlines)\n\n        // Parse controller names and convert to our enum\n        let availableControllers =\n            controllersContent\n            .split(separator: \" \")\n            .compactMap { Cgroup2Controller(rawValue: String($0)) }\n\n        if !availableControllers.isEmpty {\n            try toggleSubtreeControllers(controllers: availableControllers, enable: enable)\n        }\n    }\n\n    package func addProcess(pid: Int32) throws {\n        self.logger?.debug(\n            \"adding new proc to cgroup\",\n            metadata: [\n                \"mountpoint\": \"\\(self.mountPoint.path)\",\n                \"path\": \"\\(self.path.path)\",\n            ])\n\n        let pidStr = String(pid)\n        try Self.writeValue(\n            path: self.path,\n            value: pidStr,\n            fileName: Self.procsFile\n        )\n    }\n\n    package func applyResources(resources: ContainerizationOCI.LinuxResources) throws {\n        self.logger?.debug(\n            \"applying cgroup resources\",\n            metadata: [\n                \"path\": \"\\(self.path.path)\"\n            ])\n\n        if let memory = resources.memory, let limit = memory.limit {\n            try Self.writeValue(\n                path: self.path,\n                value: String(limit),\n                fileName: \"memory.max\"\n            )\n        }\n\n        if let cpu = resources.cpu {\n            if let quota = cpu.quota, let period = cpu.period {\n                // cpu.max format is \"quota period\"\n                let value = \"\\(quota) \\(period)\"\n                try Self.writeValue(\n                    path: self.path,\n                    value: value,\n                    fileName: \"cpu.max\"\n                )\n            }\n        }\n\n        if let pids = resources.pids {\n            try Self.writeValue(\n                path: self.path,\n                value: String(pids.limit),\n                fileName: \"pids.max\"\n            )\n        }\n    }\n\n    package func setMemoryHigh(bytes: UInt64) throws {\n        self.logger?.debug(\n            \"setting memory.high\",\n            metadata: [\n                \"path\": \"\\(self.path.path)\",\n                \"bytes\": \"\\(bytes)\",\n            ])\n\n        try Self.writeValue(\n            path: self.path,\n            value: String(bytes),\n            fileName: \"memory.high\"\n        )\n    }\n\n    package func getMemoryEvents() throws -> MemoryEvents {\n        let content = try readFileContent(fileName: \"memory.events\")\n        let values = parseKeyValuePairs(content)\n\n        return MemoryEvents(\n            low: values[\"low\"] ?? 0,\n            high: values[\"high\"] ?? 0,\n            max: values[\"max\"] ?? 0,\n            oom: values[\"oom\"] ?? 0,\n            oomKill: values[\"oom_kill\"] ?? 0\n        )\n    }\n\n    package func getMemoryEventsPath() -> String {\n        self.path.appending(path: \"memory.events\").path\n    }\n\n    package func kill() throws {\n        try Self.writeValue(\n            path: self.path,\n            value: \"1\",\n            fileName: Self.killFile\n        )\n    }\n\n    package func delete(force: Bool = false) throws {\n        self.logger?.info(\n            \"deleting cgroup manager\",\n            metadata: [\n                \"mountpoint\": \"\\(self.mountPoint.path)\",\n                \"path\": \"\\(self.path.path)\",\n            ])\n\n        if force {\n            try self.kill()\n        }\n\n        // Recursively remove child cgroups first\n        try removeChildCgroups(at: self.path, force: force)\n\n        let result = rmdir(self.path.path)\n        if result != 0 {\n            throw Error.errno(errno: errno, message: \"failed to remove cgroup directory \\(self.path.path)\")\n        }\n    }\n\n    private func removeChildCgroups(at path: URL, force: Bool) throws {\n        let fileManager = FileManager.default\n\n        guard let contents = try? fileManager.contentsOfDirectory(atPath: path.path) else {\n            return\n        }\n\n        // Remove child directories (potential nested cgroups) first\n        for item in contents {\n            let childPath = path.appending(path: item)\n            var isDirectory: ObjCBool = false\n\n            if fileManager.fileExists(atPath: childPath.path, isDirectory: &isDirectory) && isDirectory.boolValue {\n                if force {\n                    try Self.writeValue(\n                        path: childPath,\n                        value: \"1\",\n                        fileName: Self.killFile\n                    )\n                }\n\n                try removeChildCgroups(at: childPath, force: force)\n                let result = rmdir(childPath.path)\n                if result != 0 {\n                    throw Error.errno(errno: errno, message: \"failed to remove child cgroup \\(childPath.path)\")\n                }\n            }\n        }\n    }\n\n    package func stats() throws -> Cgroup2Stats {\n        let pidsStats = try self.readPidsStats()\n        let memoryStats = try self.readMemoryStats()\n        let cpuStats = try self.readCPUStats()\n        let ioStats = try self.readIOStats()\n\n        return Cgroup2Stats(\n            pids: pidsStats,\n            memory: memoryStats,\n            cpu: cpuStats,\n            io: ioStats\n        )\n    }\n\n    private func readFileContent(fileName: String) throws -> String? {\n        let filePath = self.path.appending(path: fileName)\n        guard FileManager.default.fileExists(atPath: filePath.path) else {\n            return nil\n        }\n        return try String(contentsOf: filePath, encoding: .utf8)\n            .trimmingCharacters(in: .whitespacesAndNewlines)\n    }\n\n    private func parseSingleValue(_ content: String?) -> UInt64? {\n        guard let content = content, !content.isEmpty else { return nil }\n        if content == \"max\" {\n            return UInt64.max\n        }\n        return UInt64(content)\n    }\n\n    private func parseKeyValuePairs(_ content: String?) -> [String: UInt64] {\n        guard let content = content else { return [:] }\n        var result: [String: UInt64] = [:]\n\n        for line in content.components(separatedBy: .newlines) {\n            let parts = line.components(separatedBy: .whitespaces)\n            if parts.count == 2, let value = UInt64(parts[1]) {\n                result[parts[0]] = value\n            }\n        }\n        return result\n    }\n\n    private func readPidsStats() throws -> PidsStats? {\n        guard let currentContent = try readFileContent(fileName: \"pids.current\"),\n            let current = parseSingleValue(currentContent)\n        else {\n            return nil\n        }\n\n        let maxContent = try readFileContent(fileName: \"pids.max\")\n        let max = parseSingleValue(maxContent)\n\n        return PidsStats(current: current, max: max)\n    }\n\n    private func readMemoryStats() throws -> MemoryStats? {\n        guard let usageContent = try readFileContent(fileName: \"memory.current\"),\n            let usage = parseSingleValue(usageContent)\n        else {\n            return nil\n        }\n\n        let usageLimit = parseSingleValue(try readFileContent(fileName: \"memory.max\"))\n        let swapUsage = parseSingleValue(try readFileContent(fileName: \"memory.swap.current\"))\n        let swapLimit = parseSingleValue(try readFileContent(fileName: \"memory.swap.max\"))\n\n        let statContent = try readFileContent(fileName: \"memory.stat\")\n        let statValues = parseKeyValuePairs(statContent)\n\n        return MemoryStats(\n            usage: usage,\n            usageLimit: usageLimit,\n            swapUsage: swapUsage,\n            swapLimit: swapLimit,\n            anon: statValues[\"anon\"] ?? 0,\n            file: statValues[\"file\"] ?? 0,\n            kernelStack: statValues[\"kernel_stack\"] ?? 0,\n            slab: statValues[\"slab\"] ?? 0,\n            sock: statValues[\"sock\"] ?? 0,\n            shmem: statValues[\"shmem\"] ?? 0,\n            fileMapped: statValues[\"file_mapped\"] ?? 0,\n            fileDirty: statValues[\"file_dirty\"] ?? 0,\n            fileWriteback: statValues[\"file_writeback\"] ?? 0,\n            pgfault: statValues[\"pgfault\"] ?? 0,\n            pgmajfault: statValues[\"pgmajfault\"] ?? 0,\n            workingsetRefault: statValues[\"workingset_refault\"] ?? 0,\n            workingsetActivate: statValues[\"workingset_activate\"] ?? 0,\n            workingsetNodereclaim: statValues[\"workingset_nodereclaim\"] ?? 0,\n            inactiveAnon: statValues[\"inactive_anon\"] ?? 0,\n            activeAnon: statValues[\"active_anon\"] ?? 0,\n            inactiveFile: statValues[\"inactive_file\"] ?? 0,\n            activeFile: statValues[\"active_file\"] ?? 0\n        )\n    }\n\n    private func readCPUStats() throws -> CPUStats? {\n        let statContent = try readFileContent(fileName: \"cpu.stat\")\n        let statValues = parseKeyValuePairs(statContent)\n\n        guard !statValues.isEmpty else {\n            return nil\n        }\n\n        return CPUStats(\n            usageUsec: statValues[\"usage_usec\"] ?? 0,\n            userUsec: statValues[\"user_usec\"] ?? 0,\n            systemUsec: statValues[\"system_usec\"] ?? 0,\n            nrPeriods: statValues[\"nr_periods\"] ?? 0,\n            nrThrottled: statValues[\"nr_throttled\"] ?? 0,\n            throttledUsec: statValues[\"throttled_usec\"] ?? 0\n        )\n    }\n\n    private func readIOStats() throws -> IOStats? {\n        guard let statContent = try readFileContent(fileName: \"io.stat\") else {\n            return IOStats(entries: [])\n        }\n\n        var entries: [IOEntry] = []\n\n        for line in statContent.components(separatedBy: .newlines) {\n            guard !line.isEmpty else { continue }\n\n            let parts = line.components(separatedBy: .whitespaces)\n            guard parts.count >= 2 else { continue }\n\n            let deviceParts = parts[0].components(separatedBy: \":\")\n            guard deviceParts.count == 2,\n                let major = UInt64(deviceParts[0]),\n                let minor = UInt64(deviceParts[1])\n            else {\n                continue\n            }\n\n            var rbytes: UInt64 = 0\n            var wbytes: UInt64 = 0\n            var rios: UInt64 = 0\n            var wios: UInt64 = 0\n            var dbytes: UInt64 = 0\n            var dios: UInt64 = 0\n\n            for i in 1..<parts.count {\n                let keyValue = parts[i].components(separatedBy: \"=\")\n                guard keyValue.count == 2, let value = UInt64(keyValue[1]) else { continue }\n\n                switch keyValue[0] {\n                case \"rbytes\":\n                    rbytes = value\n                case \"wbytes\":\n                    wbytes = value\n                case \"rios\":\n                    rios = value\n                case \"wios\":\n                    wios = value\n                case \"dbytes\":\n                    dbytes = value\n                case \"dios\":\n                    dios = value\n                default:\n                    break\n                }\n            }\n\n            entries.append(\n                IOEntry(\n                    major: major,\n                    minor: minor,\n                    rbytes: rbytes,\n                    wbytes: wbytes,\n                    rios: rios,\n                    wios: wios,\n                    dbytes: dbytes,\n                    dios: dios\n                ))\n        }\n\n        return IOStats(entries: entries)\n    }\n}\n\npackage struct Cgroup2Stats: Sendable {\n    package var pids: PidsStats?\n    package var memory: MemoryStats?\n    package var cpu: CPUStats?\n    package var io: IOStats?\n\n    package init(\n        pids: PidsStats? = nil,\n        memory: MemoryStats? = nil,\n        cpu: CPUStats? = nil,\n        io: IOStats? = nil\n    ) {\n        self.pids = pids\n        self.memory = memory\n        self.cpu = cpu\n        self.io = io\n    }\n}\n\npackage struct PidsStats: Sendable {\n    package var current: UInt64\n    package var max: UInt64?\n\n    package init(current: UInt64, max: UInt64? = nil) {\n        self.current = current\n        self.max = max\n    }\n}\n\npackage struct MemoryStats: Sendable {\n    package var usage: UInt64\n    package var usageLimit: UInt64?\n    package var swapUsage: UInt64?\n    package var swapLimit: UInt64?\n\n    package var anon: UInt64\n    package var file: UInt64\n    package var kernelStack: UInt64\n    package var slab: UInt64\n    package var sock: UInt64\n    package var shmem: UInt64\n    package var fileMapped: UInt64\n    package var fileDirty: UInt64\n    package var fileWriteback: UInt64\n\n    package var pgfault: UInt64\n    package var pgmajfault: UInt64\n\n    package var workingsetRefault: UInt64\n    package var workingsetActivate: UInt64\n    package var workingsetNodereclaim: UInt64\n\n    package var inactiveAnon: UInt64\n    package var activeAnon: UInt64\n    package var inactiveFile: UInt64\n    package var activeFile: UInt64\n\n    package init(\n        usage: UInt64,\n        usageLimit: UInt64? = nil,\n        swapUsage: UInt64? = nil,\n        swapLimit: UInt64? = nil,\n        anon: UInt64 = 0,\n        file: UInt64 = 0,\n        kernelStack: UInt64 = 0,\n        slab: UInt64 = 0,\n        sock: UInt64 = 0,\n        shmem: UInt64 = 0,\n        fileMapped: UInt64 = 0,\n        fileDirty: UInt64 = 0,\n        fileWriteback: UInt64 = 0,\n        pgfault: UInt64 = 0,\n        pgmajfault: UInt64 = 0,\n        workingsetRefault: UInt64 = 0,\n        workingsetActivate: UInt64 = 0,\n        workingsetNodereclaim: UInt64 = 0,\n        inactiveAnon: UInt64 = 0,\n        activeAnon: UInt64 = 0,\n        inactiveFile: UInt64 = 0,\n        activeFile: UInt64 = 0\n    ) {\n        self.usage = usage\n        self.usageLimit = usageLimit\n        self.swapUsage = swapUsage\n        self.swapLimit = swapLimit\n        self.anon = anon\n        self.file = file\n        self.kernelStack = kernelStack\n        self.slab = slab\n        self.sock = sock\n        self.shmem = shmem\n        self.fileMapped = fileMapped\n        self.fileDirty = fileDirty\n        self.fileWriteback = fileWriteback\n        self.pgfault = pgfault\n        self.pgmajfault = pgmajfault\n        self.workingsetRefault = workingsetRefault\n        self.workingsetActivate = workingsetActivate\n        self.workingsetNodereclaim = workingsetNodereclaim\n        self.inactiveAnon = inactiveAnon\n        self.activeAnon = activeAnon\n        self.inactiveFile = inactiveFile\n        self.activeFile = activeFile\n    }\n}\n\npackage struct CPUStats: Sendable {\n    package var usageUsec: UInt64\n    package var userUsec: UInt64\n    package var systemUsec: UInt64\n    package var nrPeriods: UInt64\n    package var nrThrottled: UInt64\n    package var throttledUsec: UInt64\n\n    package init(\n        usageUsec: UInt64 = 0,\n        userUsec: UInt64 = 0,\n        systemUsec: UInt64 = 0,\n        nrPeriods: UInt64 = 0,\n        nrThrottled: UInt64 = 0,\n        throttledUsec: UInt64 = 0\n    ) {\n        self.usageUsec = usageUsec\n        self.userUsec = userUsec\n        self.systemUsec = systemUsec\n        self.nrPeriods = nrPeriods\n        self.nrThrottled = nrThrottled\n        self.throttledUsec = throttledUsec\n    }\n}\n\npackage struct IOStats: Sendable {\n    package var entries: [IOEntry]\n\n    package init(entries: [IOEntry] = []) {\n        self.entries = entries\n    }\n}\n\npackage struct IOEntry: Sendable {\n    package var major: UInt64\n    package var minor: UInt64\n    package var rbytes: UInt64\n    package var wbytes: UInt64\n    package var rios: UInt64\n    package var wios: UInt64\n    package var dbytes: UInt64\n    package var dios: UInt64\n\n    package init(\n        major: UInt64,\n        minor: UInt64,\n        rbytes: UInt64 = 0,\n        wbytes: UInt64 = 0,\n        rios: UInt64 = 0,\n        wios: UInt64 = 0,\n        dbytes: UInt64 = 0,\n        dios: UInt64 = 0\n    ) {\n        self.major = major\n        self.minor = minor\n        self.rbytes = rbytes\n        self.wbytes = wbytes\n        self.rios = rios\n        self.wios = wios\n        self.dbytes = dbytes\n        self.dios = dios\n    }\n}\n\npackage struct MemoryEvents: Sendable {\n    package var low: UInt64\n    package var high: UInt64\n    package var max: UInt64\n    package var oom: UInt64\n    package var oomKill: UInt64\n\n    package init(\n        low: UInt64 = 0,\n        high: UInt64 = 0,\n        max: UInt64 = 0,\n        oom: UInt64 = 0,\n        oomKill: UInt64 = 0\n    ) {\n        self.low = low\n        self.high = high\n        self.max = max\n        self.oom = oom\n        self.oomKill = oomKill\n    }\n}\n\nextension Cgroup2Manager {\n    package enum Error: Swift.Error, CustomStringConvertible {\n        case notCgroup\n        case cgroup1\n        case errno(errno: Int32, message: String)\n        case notExist(path: String)\n\n        package var description: String {\n            switch self {\n            case .errno(let errno, let message):\n                return \"failed with errno \\(errno): \\(message)\"\n            case .notExist(let path):\n                return \"cgroup at path \\(path) does not exist\"\n            case .cgroup1:\n                return \"tried to load a cgroup v1 path\"\n            case .notCgroup:\n                return \"path is not a cgroup mountpoint\"\n            }\n        }\n    }\n}\n\n#endif\n"
  },
  {
    "path": "vminitd/Sources/LCShim/include/syscall.h",
    "content": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __SYSCALL_H\n#define __SYSCALL_H\n\n#include <sys/types.h>\n\nint CZ_pivot_root(const char *new_root, const char *put_old);\n\nint CZ_set_sub_reaper();\n\n#ifndef SYS_pidfd_open\n#define SYS_pidfd_open 434\n#endif\n\nint CZ_pidfd_open(pid_t pid, unsigned int flags);\n\n#ifndef SYS_pidfd_getfd\n#define SYS_pidfd_getfd 438\n#endif\n\nint CZ_pidfd_getfd(int pidfd, int targetfd, unsigned int flags);\n\nint CZ_prctl_set_no_new_privs();\n\n#endif\n"
  },
  {
    "path": "vminitd/Sources/LCShim/syscall.c",
    "content": "/*\n * Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sys/prctl.h>\n#include <sys/syscall.h>\n#include <unistd.h>\n\n#include \"syscall.h\"\n\nint CZ_pivot_root(const char *new_root, const char *put_old) {\n  return syscall(SYS_pivot_root, new_root, put_old);\n}\n\nint CZ_set_sub_reaper() { return prctl(PR_SET_CHILD_SUBREAPER, 1); }\n\nint CZ_pidfd_open(pid_t pid, unsigned int flags) {\n  // Musl doesn't have pidfd_open.\n  return syscall(SYS_pidfd_open, pid, flags);\n}\n\nint CZ_pidfd_getfd(int pidfd, int targetfd, unsigned int flags) {\n  // Musl doesn't have pidfd_getfd.\n  return syscall(SYS_pidfd_getfd, pidfd, targetfd, flags);\n}\n\nint CZ_prctl_set_no_new_privs() {\n  return prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);\n}\n"
  },
  {
    "path": "vminitd/Sources/vmexec/Console.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport FoundationEssentials\nimport Musl\n\nclass Console {\n    let master: Int32\n    let slavePath: String\n\n    init() throws {\n        let masterFD = open(\"/dev/ptmx\", O_RDWR | O_NOCTTY | O_CLOEXEC)\n        guard masterFD != -1 else {\n            throw App.Errno(stage: \"open_ptmx\")\n        }\n\n        guard unlockpt(masterFD) == 0 else {\n            throw App.Errno(stage: \"unlockpt\")\n        }\n\n        guard let slavePath = ptsname(masterFD) else {\n            throw App.Errno(stage: \"ptsname\")\n        }\n\n        self.master = masterFD\n        self.slavePath = String(cString: slavePath)\n    }\n\n    func configureStdIO() throws {\n        let path = self.slavePath\n        let slaveFD = open(path, O_RDWR)\n        guard slaveFD != -1 else {\n            throw App.Errno(stage: \"open_pts\")\n        }\n        defer { Musl.close(slaveFD) }\n\n        for fd: Int32 in 0...2 {\n            guard dup3(slaveFD, fd, 0) != -1 else {\n                throw App.Errno(stage: \"dup3\")\n            }\n        }\n    }\n\n    func close() throws {\n        guard Musl.close(self.master) == 0 else {\n            throw App.Errno(stage: \"close\")\n        }\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vmexec/ExecCommand.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ArgumentParser\nimport ContainerizationOCI\nimport ContainerizationOS\nimport FoundationEssentials\nimport LCShim\nimport Logging\nimport Musl\nimport SystemPackage\n\nstruct ExecCommand: ParsableCommand {\n    static let configuration = CommandConfiguration(\n        commandName: \"exec\",\n        abstract: \"Exec in a container\"\n    )\n\n    @Option(name: .long, help: \"path to an OCI runtime spec process configuration\")\n    var processPath: String\n\n    @Option(name: .long, help: \"pid of the init process for the container\")\n    var parentPid: Int\n\n    func run() throws {\n        do {\n            let src = URL(fileURLWithPath: processPath)\n            let processBytes = try Data(contentsOf: src)\n            let process = try JSONDecoder().decode(\n                ContainerizationOCI.Process.self,\n                from: processBytes\n            )\n            try execInNamespaces(process: process)\n        } catch {\n            App.writeError(error)\n            throw error\n        }\n    }\n\n    static func enterNS(pidFd: Int32, nsType: Int32) throws {\n        guard setns(pidFd, nsType) == 0 else {\n            throw App.Errno(stage: \"setns(fd)\")\n        }\n    }\n\n    private func execInNamespaces(process: ContainerizationOCI.Process) throws {\n        let syncPipe = FileDescriptor(rawValue: 3)\n        let ackPipe = FileDescriptor(rawValue: 4)\n\n        let pidFd = CZ_pidfd_open(Int32(parentPid), 0)\n        guard pidFd > 0 else {\n            throw App.Errno(stage: \"pidfd_open(\\(parentPid))\")\n        }\n        try Self.enterNS(\n            pidFd: pidFd,\n            nsType: CLONE_NEWCGROUP | CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNS\n        )\n\n        let processID = fork()\n\n        guard processID != -1 else {\n            try? syncPipe.close()\n            try? ackPipe.close()\n\n            throw App.Errno(stage: \"fork\")\n        }\n\n        if processID == 0 {  // child\n            // Wait for the grandparent to tell us that they acked our pid.\n            var pidAckBuffer = [UInt8](repeating: 0, count: App.ackPid.count)\n            let pidAckBytesRead = try pidAckBuffer.withUnsafeMutableBytes { buffer in\n                try ackPipe.read(into: buffer)\n            }\n            guard pidAckBytesRead > 0 else {\n                throw App.Failure(message: \"read ack pipe\")\n            }\n            let pidAckStr = String(decoding: pidAckBuffer[..<pidAckBytesRead], as: UTF8.self)\n\n            guard pidAckStr == App.ackPid else {\n                throw App.Failure(message: \"received invalid acknowledgement string: \\(pidAckStr)\")\n            }\n\n            guard setsid() != -1 else {\n                throw App.Errno(stage: \"setsid()\")\n            }\n\n            if process.terminal {\n                let pty = try Console()\n                try pty.configureStdIO()\n                var masterFD = pty.master\n\n                try withUnsafeBytes(of: &masterFD) { bytes in\n                    _ = try syncPipe.write(bytes)\n                }\n\n                // Wait for the grandparent to tell us that they acked our console.\n                var consoleAckBuffer = [UInt8](repeating: 0, count: App.ackConsole.count)\n                let consoleAckBytesRead = try consoleAckBuffer.withUnsafeMutableBytes { buffer in\n                    try ackPipe.read(into: buffer)\n                }\n                guard consoleAckBytesRead > 0 else {\n                    throw App.Failure(message: \"read ack pipe\")\n                }\n                let consoleAckStr = String(decoding: consoleAckBuffer[..<consoleAckBytesRead], as: UTF8.self)\n\n                guard consoleAckStr == App.ackConsole else {\n                    throw App.Failure(message: \"received invalid acknowledgement string: \\(consoleAckStr)\")\n                }\n\n                guard ioctl(0, UInt(TIOCSCTTY), 0) != -1 else {\n                    throw App.Errno(stage: \"setctty(0)\")\n                }\n                try pty.close()\n            }\n\n            // Apply O_CLOEXEC to all file descriptors except stdio.\n            // This ensures that all unwanted fds we may have accidentally\n            // inherited are marked close-on-exec so they stay out of the\n            // container.\n            try App.applyCloseExecOnFDs()\n            try App.setRLimits(rlimits: process.rlimits)\n\n            // Prepare capabilities (before user change)\n            let preparedCaps = try App.prepareCapabilities(capabilities: process.capabilities ?? ContainerizationOCI.LinuxCapabilities())\n\n            // Change stdio to be owned by the requested user.\n            try App.fixStdioPerms(user: process.user)\n\n            // Set uid, gid, and supplementary groups\n            try App.setPermissions(user: process.user)\n\n            // Finish capabilities (after user change)\n            try App.finishCapabilities(preparedCaps)\n\n            // Set no_new_privs if requested by the OCI spec.\n            try App.setNoNewPrivileges(process: process)\n\n            try App.exec(process: process, currentEnv: process.env)\n        } else {  // parent process\n            // Send our child's pid to our parent before we exit.\n            var childPid = processID\n            try withUnsafeBytes(of: &childPid) { bytes in\n                _ = try syncPipe.write(bytes)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vmexec/Mount.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOCI\nimport ContainerizationOS\nimport FoundationEssentials\nimport Musl\n\nstruct ContainerMount {\n    private let mounts: [ContainerizationOCI.Mount]\n    private let rootfs: String\n\n    init(rootfs: String, mounts: [ContainerizationOCI.Mount]) {\n        self.rootfs = rootfs\n        self.mounts = mounts\n    }\n\n    func mountToRootfs() throws {\n        for m in self.mounts {\n            let osMount = m.toOSMount()\n            try osMount.mount(root: self.rootfs)\n        }\n    }\n\n    func configureConsole() throws {\n        let ptmx = rootfs + \"/dev/ptmx\"\n        guard remove(ptmx) == 0 else {\n            throw App.Errno(stage: \"remove(ptmx)\")\n        }\n        guard symlink(\"pts/ptmx\", ptmx) == 0 else {\n            throw App.Errno(stage: \"symlink(pts/ptmx)\")\n        }\n    }\n}\n\nextension ContainerizationOCI.Mount {\n    func toOSMount() -> ContainerizationOS.Mount {\n        ContainerizationOS.Mount(\n            type: self.type,\n            source: self.source,\n            target: self.destination,\n            options: self.options\n        )\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vmexec/RunCommand.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ArgumentParser\nimport Cgroup\nimport ContainerizationOCI\nimport ContainerizationOS\nimport FoundationEssentials\nimport LCShim\nimport Musl\nimport SystemPackage\n\nstruct RunCommand: ParsableCommand {\n    static let configuration = CommandConfiguration(\n        commandName: \"run\",\n        abstract: \"Run a container\"\n    )\n\n    @Option(name: .long, help: \"path to an OCI bundle\")\n    var bundlePath: String\n\n    mutating func run() throws {\n        do {\n            let spec: ContainerizationOCI.Spec\n            do {\n                let bundle = try ContainerizationOCI.Bundle.load(path: URL(filePath: bundlePath))\n                spec = try bundle.loadConfig()\n            } catch {\n                throw App.Failure(message: \"failed to load OCI bundle at \\(bundlePath): \\(error)\")\n            }\n            try execInNamespace(spec: spec)\n        } catch {\n            App.writeError(error)\n            throw error\n        }\n    }\n\n    private func childRootSetup(rootfs: ContainerizationOCI.Root, mounts: [ContainerizationOCI.Mount]) throws {\n        // setup rootfs\n        try prepareRoot(rootfs: rootfs.path)\n        try mountRootfs(rootfs: rootfs.path, mounts: mounts)\n        try setDevSymlinks(rootfs: rootfs.path)\n\n        try pivotRoot(rootfs: rootfs.path)\n\n        // Remount ro if requested.\n        if rootfs.readonly {\n            try self.remountRootfsReadOnly()\n        }\n\n        try reOpenDevNull()\n    }\n\n    private func remountRootfsReadOnly() throws {\n        var flags = UInt(MS_BIND | MS_REMOUNT | MS_RDONLY)\n\n        let ret = mount(\"\", \"/\", \"\", flags, \"\")\n        if ret == 0 {\n            return\n        }\n\n        var s = statfs()\n        guard statfs(\"/\", &s) == 0 else {\n            throw App.Errno(stage: \"statfs(/)\")\n        }\n        flags |= s.f_flags\n\n        guard mount(\"\", \"/\", \"\", flags, \"\") == 0 else {\n            throw App.Errno(stage: \"mount rootfs ro\")\n        }\n    }\n\n    private func childSetup(\n        spec: ContainerizationOCI.Spec,\n        ackPipe: FileDescriptor,\n        syncPipe: FileDescriptor\n    ) throws {\n        guard let process = spec.process else {\n            throw App.Failure(message: \"no process configuration found in runtime spec\")\n        }\n        guard let root = spec.root else {\n            throw App.Failure(message: \"no root found in runtime spec\")\n        }\n\n        // Wait for the grandparent to tell us that they acked our pid.\n        var pidAckBuffer = [UInt8](repeating: 0, count: App.ackPid.count)\n        let pidAckBytesRead = try pidAckBuffer.withUnsafeMutableBytes { buffer in\n            try ackPipe.read(into: buffer)\n        }\n        guard pidAckBytesRead > 0 else {\n            throw App.Failure(message: \"read ack pipe\")\n        }\n        let pidAckStr = String(decoding: pidAckBuffer[..<pidAckBytesRead], as: UTF8.self)\n\n        guard pidAckStr == App.ackPid else {\n            throw App.Failure(message: \"received invalid acknowledgement string: \\(pidAckStr)\")\n        }\n\n        guard unshare(CLONE_NEWCGROUP) == 0 else {\n            throw App.Errno(stage: \"unshare(cgroup)\")\n        }\n\n        guard setsid() != -1 else {\n            throw App.Errno(stage: \"setsid()\")\n        }\n\n        try childRootSetup(rootfs: root, mounts: spec.mounts)\n\n        if process.terminal {\n            let pty = try Console()\n            try pty.configureStdIO()\n            var masterFD = pty.master\n\n            try withUnsafeBytes(of: &masterFD) { bytes in\n                _ = try syncPipe.write(bytes)\n            }\n\n            // Wait for the grandparent to tell us that they acked our console.\n            var consoleAckBuffer = [UInt8](repeating: 0, count: App.ackConsole.count)\n            let consoleAckBytesRead = try consoleAckBuffer.withUnsafeMutableBytes { buffer in\n                try ackPipe.read(into: buffer)\n            }\n            guard consoleAckBytesRead > 0 else {\n                throw App.Failure(message: \"read ack pipe\")\n            }\n            let consoleAckStr = String(decoding: consoleAckBuffer[..<consoleAckBytesRead], as: UTF8.self)\n\n            guard consoleAckStr == App.ackConsole else {\n                throw App.Failure(message: \"received invalid acknowledgement string: \\(consoleAckStr)\")\n            }\n\n            guard ioctl(0, UInt(TIOCSCTTY), 0) != -1 else {\n                throw App.Errno(stage: \"setctty(0)\")\n            }\n\n            try mountConsole(path: pty.slavePath)\n            try pty.close()\n        }\n\n        if !spec.hostname.isEmpty {\n            let errCode = spec.hostname.withCString { ptr in\n                Musl.sethostname(ptr, spec.hostname.count)\n            }\n            guard errCode == 0 else {\n                throw App.Errno(stage: \"sethostname()\")\n            }\n        }\n\n        // Apply sysctls from the OCI spec.\n        if let sysctls = spec.linux?.sysctl {\n            for (key, value) in sysctls {\n                let path = \"/proc/sys/\" + key.replacingOccurrences(of: \".\", with: \"/\")\n                let fd = open(path, O_WRONLY)\n                guard fd >= 0 else {\n                    throw App.Errno(stage: \"sysctl open(\\(path))\")\n                }\n                defer { close(fd) }\n                let bytes = Array(value.utf8)\n                let written = write(fd, bytes, bytes.count)\n                guard written == bytes.count else {\n                    throw App.Errno(stage: \"sysctl write(\\(key)=\\(value))\")\n                }\n            }\n        }\n\n        // Apply O_CLOEXEC to all file descriptors except stdio.\n        // This ensures that all unwanted fds we may have accidentally\n        // inherited are marked close-on-exec so they stay out of the\n        // container.\n        try App.applyCloseExecOnFDs()\n\n        try App.setRLimits(rlimits: process.rlimits)\n\n        // Prepare capabilities (before user change)\n        let preparedCaps = try App.prepareCapabilities(capabilities: process.capabilities ?? ContainerizationOCI.LinuxCapabilities())\n\n        // Change stdio to be owned by the requested user.\n        try App.fixStdioPerms(user: process.user)\n\n        // Set uid, gid, and supplementary groups.\n        try App.setPermissions(user: process.user)\n\n        // Finish capabilities (after user change)\n        try App.finishCapabilities(preparedCaps)\n\n        // Set no_new_privs if requested by the OCI spec.\n        try App.setNoNewPrivileges(process: process)\n\n        // Finally execve the container process.\n        try App.exec(process: process, currentEnv: process.env)\n    }\n\n    private func setupNamespaces(namespaces: [ContainerizationOCI.LinuxNamespace]?) throws -> Int32 {\n        var unshareFlags: Int32 = 0\n\n        // Map namespace types to their corresponding CLONE flags\n        let nsTypeToFlag: [ContainerizationOCI.LinuxNamespaceType: Int32] = [\n            .pid: CLONE_NEWPID,\n            .mount: CLONE_NEWNS,\n            .uts: CLONE_NEWUTS,\n            .ipc: CLONE_NEWIPC,\n            .user: CLONE_NEWUSER,\n            .cgroup: CLONE_NEWCGROUP,\n        ]\n\n        guard let namespaces = namespaces else {\n            return CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWUTS\n        }\n\n        for ns in namespaces {\n            guard let flag = nsTypeToFlag[ns.type] else {\n                continue\n            }\n\n            if ns.path.isEmpty {\n                unshareFlags |= flag\n            } else {\n                let fd = open(ns.path, O_RDONLY | O_CLOEXEC)\n                guard fd >= 0 else {\n                    throw App.Errno(stage: \"open(\\(ns.path))\")\n                }\n                defer { close(fd) }\n\n                guard setns(fd, flag) == 0 else {\n                    throw App.Errno(stage: \"setns(\\(ns.path))\")\n                }\n            }\n        }\n\n        return unshareFlags\n    }\n\n    private func execInNamespace(spec: ContainerizationOCI.Spec) throws {\n        let syncPipe = FileDescriptor(rawValue: 3)\n        let ackPipe = FileDescriptor(rawValue: 4)\n\n        let unshareFlags = try setupNamespaces(namespaces: spec.linux?.namespaces)\n\n        guard unshare(unshareFlags) == 0 else {\n            throw App.Errno(stage: \"unshare(\\(unshareFlags))\")\n        }\n\n        let processID = fork()\n        guard processID != -1 else {\n            try? syncPipe.close()\n            try? ackPipe.close()\n            throw App.Errno(stage: \"fork\")\n        }\n\n        if processID == 0 {  // child\n            try childSetup(spec: spec, ackPipe: ackPipe, syncPipe: syncPipe)\n        } else {  // parent process\n            // Setup cgroup before child enters cgroup namespace\n            if let linux = spec.linux {\n                let cgroupPath = linux.cgroupsPath\n                if !cgroupPath.isEmpty {\n                    let cgroupManager = try Cgroup2Manager.load(group: URL(filePath: cgroupPath))\n\n                    if let resources = linux.resources {\n                        try cgroupManager.applyResources(resources: resources)\n                    }\n\n                    try cgroupManager.addProcess(pid: processID)\n                }\n            }\n\n            // Send our child's pid before we exit.\n            var childPid = processID\n            try withUnsafeBytes(of: &childPid) { bytes in\n                _ = try syncPipe.write(bytes)\n            }\n        }\n    }\n\n    private func mountRootfs(rootfs: String, mounts: [ContainerizationOCI.Mount]) throws {\n        let containerMount = ContainerMount(rootfs: rootfs, mounts: mounts)\n        try containerMount.mountToRootfs()\n        try containerMount.configureConsole()\n    }\n\n    private func prepareRoot(rootfs: String) throws {\n        guard mount(\"\", \"/\", \"\", UInt(MS_SLAVE | MS_REC), nil) == 0 else {\n            throw App.Errno(stage: \"mount(slave|rec)\")\n        }\n\n        guard mount(rootfs, rootfs, \"bind\", UInt(MS_BIND | MS_REC), nil) == 0 else {\n            throw App.Errno(stage: \"mount(bind|rec)\")\n        }\n    }\n\n    private func setDevSymlinks(rootfs: String) throws {\n        let links: [(src: String, dst: String)] = [\n            (\"/proc/self/fd\", \"/dev/fd\"),\n            (\"/proc/self/fd/0\", \"/dev/stdin\"),\n            (\"/proc/self/fd/1\", \"/dev/stdout\"),\n            (\"/proc/self/fd/2\", \"/dev/stderr\"),\n            (\"/dev/rtc0\", \"/dev/rtc\"),\n        ]\n\n        let rootfsURL = URL(fileURLWithPath: rootfs)\n        for (src, dst) in links {\n            let dest = rootfsURL.appendingPathComponent(dst)\n            guard symlink(src, dest.path) == 0 else {\n                if errno == EEXIST {\n                    continue\n                }\n                throw App.Errno(stage: \"symlink(\\(src) -> \\(dest.path))\")\n            }\n        }\n    }\n\n    private func reOpenDevNull() throws {\n        let file = open(\"/dev/null\", O_RDWR)\n        guard file != -1 else {\n            throw App.Errno(stage: \"open(/dev/null)\")\n        }\n        defer { close(file) }\n\n        var devNullStat = stat()\n        try withUnsafeMutablePointer(to: &devNullStat) { pointer in\n            guard fstat(file, pointer) == 0 else {\n                throw App.Errno(stage: \"fstat(/dev/null)\")\n            }\n        }\n\n        for fd: Int32 in 0...2 {\n            var fdStat = stat()\n            try withUnsafeMutablePointer(to: &fdStat) { pointer in\n                guard fstat(fd, pointer) == 0 else {\n                    throw App.Errno(stage: \"fstat(fd)\")\n                }\n            }\n\n            if fdStat.st_rdev == devNullStat.st_rdev {\n                guard dup3(file, fd, 0) != -1 else {\n                    throw App.Errno(stage: \"dup3(null)\")\n                }\n            }\n        }\n    }\n\n    /// Pivots the rootfs of the calling process in the namespace to the provided\n    /// rootfs in the argument.\n    ///\n    /// The pivot_root(\".\", \".\") and unmount old root approach is exactly the same\n    /// as runc's pivot root implementation in:\n    /// https://github.com/opencontainers/runc/blob/main/libcontainer/rootfs_linux.go\n    private func pivotRoot(rootfs: String) throws {\n        let oldRoot = open(\"/\", O_RDONLY | O_DIRECTORY)\n        if oldRoot <= 0 {\n            throw App.Errno(stage: \"open(oldroot)\")\n        }\n        defer { close(oldRoot) }\n\n        let newRoot = open(rootfs, O_RDONLY | O_DIRECTORY)\n        if newRoot <= 0 {\n            throw App.Errno(stage: \"open(newroot)\")\n        }\n        defer { close(newRoot) }\n\n        // change cwd to the new root\n        guard fchdir(newRoot) == 0 else {\n            throw App.Errno(stage: \"fchdir(newroot)\")\n        }\n        guard CZ_pivot_root(toCString(\".\"), toCString(\".\")) == 0 else {\n            throw App.Errno(stage: \"pivot_root()\")\n        }\n        // change cwd to the old root\n        guard fchdir(oldRoot) == 0 else {\n            throw App.Errno(stage: \"fchdir(oldroot)\")\n        }\n        // mount old root rslave so that unmount doesn't propagate back to outside\n        // the namespace\n        guard mount(\"\", \".\", \"\", UInt(MS_SLAVE | MS_REC), nil) == 0 else {\n            throw App.Errno(stage: \"mount(., slave|rec)\")\n        }\n        // unmount old root\n        guard umount2(\".\", Int32(MNT_DETACH)) == 0 else {\n            throw App.Errno(stage: \"umount(.)\")\n        }\n        // switch cwd to the new root\n        guard chdir(\"/\") == 0 else {\n            throw App.Errno(stage: \"chdir(/)\")\n        }\n    }\n\n    private func toCString(_ str: String) -> UnsafeMutablePointer<CChar>? {\n        let cString = str.utf8CString\n        let cStringCopy = UnsafeMutableBufferPointer<CChar>.allocate(capacity: cString.count)\n        _ = cStringCopy.initialize(from: cString)\n        return UnsafeMutablePointer(cStringCopy.baseAddress)\n    }\n\n    private func mountConsole(path: String) throws {\n        let console = \"/dev/console\"\n        if access(console, F_OK) != 0 {\n            let fd = open(console, O_RDWR | O_CREAT, mode_t(UInt16(0o600)))\n            guard fd != -1 else {\n                throw App.Errno(stage: \"open(/dev/console)\")\n            }\n            close(fd)\n        }\n\n        guard mount(path, console, \"bind\", UInt(MS_BIND), nil) == 0 else {\n            throw App.Errno(stage: \"mount(console)\")\n        }\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vmexec/vmexec.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n/// NOTE: This binary implements a very small subset of the OCI runtime spec, mostly just\n/// the process configurations. Mounts are somewhat functional, but masked and read only paths\n/// aren't checked today. Today the namespaces are also ignored, and we always spawn a new pid\n/// and mount namespace.\n\nimport ArgumentParser\nimport ContainerizationError\nimport ContainerizationOCI\nimport ContainerizationOS\nimport FoundationEssentials\nimport LCShim\nimport Logging\nimport Musl\nimport SystemPackage\n\n@main\nstruct App: ParsableCommand {\n    static let ackPid = \"AckPid\"\n    static let ackConsole = \"AckConsole\"\n\n    static let configuration = CommandConfiguration(\n        commandName: \"vmexec\",\n        version: \"0.1.0\",\n        subcommands: [\n            ExecCommand.self,\n            RunCommand.self,\n        ]\n    )\n}\n\nextension App {\n    /// Applies O_CLOEXEC to all file descriptors currently open for\n    /// the process except the stdio fd values\n    static func applyCloseExecOnFDs() throws {\n        let minFD = 2  // stdin, stdout, stderr should be preserved\n\n        let fdList = try FileManager.default.contentsOfDirectory(atPath: \"/proc/self/fd\")\n\n        for fdStr in fdList {\n            guard let fd = Int(fdStr) else {\n                continue\n            }\n            if fd <= minFD {\n                continue\n            }\n\n            _ = fcntl(Int32(fd), F_SETFD, FD_CLOEXEC)\n        }\n    }\n\n    static func exec(process: ContainerizationOCI.Process, currentEnv: [String]? = nil) throws {\n        guard !process.args.isEmpty else {\n            throw App.Errno(stage: \"exec\", info: \"process args cannot be empty\")\n        }\n\n        let executableArg = process.args[0]\n        let resolvedExecutable: URL\n\n        if executableArg.contains(\"/\") {\n            if executableArg.hasPrefix(\"/\") {\n                resolvedExecutable = URL(fileURLWithPath: executableArg)\n            } else {\n                resolvedExecutable = URL(fileURLWithPath: process.cwd).appendingPathComponent(executableArg).standardized\n            }\n\n            guard FileManager.default.fileExists(atPath: resolvedExecutable.path) else {\n                throw App.Failure(message: \"failed to find target executable \\(executableArg)\")\n            }\n        } else {\n            let path = Path.findPath(currentEnv) ?? Path.getCurrentPath()\n            guard let found = Path.lookPath(executableArg, path: path) else {\n                throw App.Failure(message: \"failed to find target executable \\(executableArg)\")\n            }\n            resolvedExecutable = found\n        }\n\n        let executable = strdup(resolvedExecutable.path)\n        var argv = process.args.map { strdup($0) }\n        argv += [nil]\n        let env = process.env.map { strdup($0) } + [nil]\n        let cwd = process.cwd\n\n        // Create the working directory if it doesn't exist, this seems like the expected\n        // OCI runtime spec behavior.\n        if !FileManager.default.fileExists(atPath: cwd) {\n            try FileManager.default.createDirectory(\n                atPath: cwd,\n                withIntermediateDirectories: true,\n                attributes: [.posixPermissions: 0o755]\n            )\n        }\n\n        guard chdir(cwd) == 0 else {\n            throw App.Errno(stage: \"chdir(cwd)\", info: \"failed to change directory to '\\(cwd)'\")\n        }\n\n        guard execvpe(executable, argv, env) != -1 else {\n            throw App.Errno(stage: \"execvpe(\\(String(describing: executable)))\", info: \"failed to exec [\\(process.args.joined(separator: \" \"))]\")\n        }\n        fatalError(\"execvpe failed\")\n    }\n\n    static func setPermissions(user: ContainerizationOCI.User) throws {\n        if user.additionalGids.count > 0 {\n            guard setgroups(user.additionalGids.count, user.additionalGids) == 0 else {\n                throw App.Errno(stage: \"setgroups()\")\n            }\n        }\n        guard setgid(user.gid) == 0 else {\n            throw App.Errno(stage: \"setgid()\")\n        }\n        // NOTE: setuid has to be done last because once the uid has been\n        // changed, then the process will lose privilege to set the group\n        // and supplementary groups\n        guard setuid(user.uid) == 0 else {\n            throw App.Errno(stage: \"setuid()\")\n        }\n    }\n\n    static func fixStdioPerms(user: ContainerizationOCI.User) throws {\n        for i in 0...2 {\n            var fdStat = stat()\n            try withUnsafeMutablePointer(to: &fdStat) { pointer in\n                guard fstat(Int32(i), pointer) == 0 else {\n                    throw App.Errno(stage: \"fstat(fd)\")\n                }\n            }\n\n            let desired = uid_t(user.uid)\n            if fdStat.st_uid != desired {\n                guard fchown(Int32(i), desired, fdStat.st_gid) != -1 else {\n                    throw App.Errno(stage: \"fchown(\\(i))\")\n                }\n            }\n        }\n    }\n\n    static func setRLimits(rlimits: [ContainerizationOCI.POSIXRlimit]) throws {\n        for rl in rlimits {\n            var limit = rlimit(rlim_cur: rl.soft, rlim_max: rl.hard)\n            let resource: Int32\n            switch rl.type {\n            case \"RLIMIT_AS\":\n                resource = RLIMIT_AS\n            case \"RLIMIT_CORE\":\n                resource = RLIMIT_CORE\n            case \"RLIMIT_CPU\":\n                resource = RLIMIT_CPU\n            case \"RLIMIT_DATA\":\n                resource = RLIMIT_DATA\n            case \"RLIMIT_FSIZE\":\n                resource = RLIMIT_FSIZE\n            case \"RLIMIT_LOCKS\":\n                resource = RLIMIT_LOCKS\n            case \"RLIMIT_MEMLOCK\":\n                resource = RLIMIT_MEMLOCK\n            case \"RLIMIT_MSGQUEUE\":\n                resource = RLIMIT_MSGQUEUE\n            case \"RLIMIT_NICE\":\n                resource = RLIMIT_NICE\n            case \"RLIMIT_NOFILE\":\n                resource = RLIMIT_NOFILE\n            case \"RLIMIT_NPROC\":\n                resource = RLIMIT_NPROC\n            case \"RLIMIT_RSS\":\n                resource = RLIMIT_RSS\n            case \"RLIMIT_RTPRIO\":\n                resource = RLIMIT_RTPRIO\n            case \"RLIMIT_RTTIME\":\n                resource = RLIMIT_RTTIME\n            case \"RLIMIT_SIGPENDING\":\n                resource = RLIMIT_SIGPENDING\n            case \"RLIMIT_STACK\":\n                resource = RLIMIT_STACK\n            default:\n                errno = EINVAL\n                throw App.Errno(stage: \"rlimit key unknown\")\n            }\n            guard setrlimit(resource, &limit) == 0 else {\n                throw App.Errno(stage: \"setrlimit()\")\n            }\n        }\n    }\n\n    static func prepareCapabilities(capabilities: ContainerizationOCI.LinuxCapabilities) throws -> ContainerizationOS.LinuxCapabilities? {\n        // Create capabilities instance from OCI config\n        var caps = ContainerizationOS.LinuxCapabilities()\n\n        caps.set(which: [.effective], caps: (capabilities.effective ?? []).compactMap { try? CapabilityName(rawValue: $0) })\n        caps.set(which: [.permitted], caps: (capabilities.permitted ?? []).compactMap { try? CapabilityName(rawValue: $0) })\n        caps.set(which: [.inheritable], caps: (capabilities.inheritable ?? []).compactMap { try? CapabilityName(rawValue: $0) })\n        caps.set(which: [.bounding], caps: (capabilities.bounding ?? []).compactMap { try? CapabilityName(rawValue: $0) })\n        caps.set(which: [.ambient], caps: (capabilities.ambient ?? []).compactMap { try? CapabilityName(rawValue: $0) })\n\n        // Apply bounding set BEFORE user change (drop capabilities early)\n        do {\n            try caps.apply(kind: .bounds)\n        } catch {\n            throw App.Failure(message: \"failed to apply bounding set capabilities: \\(error)\")\n        }\n\n        // Set keep caps to preserve capabilities across setuid()\n        do {\n            try LinuxCapabilities.setKeepCaps()\n        } catch {\n            throw App.Failure(message: \"failed to set keep caps: \\(error)\")\n        }\n\n        return caps\n    }\n\n    static func finishCapabilities(_ caps: ContainerizationOS.LinuxCapabilities?) throws {\n        guard let caps = caps else { return }\n\n        do {\n            try LinuxCapabilities.clearKeepCaps()\n        } catch {\n            throw App.Failure(message: \"failed to clear keep caps: \\(error)\")\n        }\n\n        do {\n            try caps.apply(kind: [.caps])\n        } catch {\n            throw App.Failure(message: \"failed to apply final capabilities: \\(error)\")\n        }\n\n        try? caps.apply(kind: [.ambs])\n    }\n\n    static func setNoNewPrivileges(process: ContainerizationOCI.Process) throws {\n        guard process.noNewPrivileges else { return }\n        guard CZ_prctl_set_no_new_privs() == 0 else {\n            throw App.Errno(stage: \"prctl(PR_SET_NO_NEW_PRIVS)\")\n        }\n    }\n\n    static func Errno(stage: String, info: String = \"\") -> ContainerizationError {\n        let posix = POSIXError(.init(rawValue: errno)!, userInfo: [\"stage\": stage])\n        return ContainerizationError(.internalError, message: \"\\(info) \\(String(describing: posix))\")\n    }\n\n    static func Failure(message: String) -> ContainerizationError {\n        ContainerizationError(\n            .internalError,\n            message: message\n        )\n    }\n\n    static func writeError(_ error: Error) {\n        let errorPipe = FileDescriptor(rawValue: 5)\n\n        let errorMessage: String\n        if let czError = error as? ContainerizationError {\n            errorMessage = czError.description\n        } else {\n            errorMessage = String(describing: error)\n        }\n\n        let bytes = Array(errorMessage.utf8)\n        _ = try? bytes.withUnsafeBytes { buffer in\n            try errorPipe.write(buffer)\n        }\n        try? errorPipe.close()\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/AgentCommand.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ArgumentParser\nimport Cgroup\nimport Containerization\nimport ContainerizationError\nimport ContainerizationOS\nimport Foundation\nimport Logging\nimport NIOCore\nimport NIOPosix\n\n#if os(Linux)\nimport Musl\nimport LCShim\n#endif\n\nstruct AgentCommand: AsyncParsableCommand {\n    static let configuration = CommandConfiguration(\n        commandName: \"agent\",\n        abstract: \"Run the vminitd agent daemon\"\n    )\n\n    private static let foregroundEnvVar = \"FOREGROUND\"\n    private static let vsockPort = 1024\n\n    @OptionGroup var options: LogLevelOption\n\n    mutating func run() async throws {\n        let log = makeLogger(label: \"vminitd\", level: options.resolvedLogLevel())\n        try Self.adjustLimits(log)\n\n        // when running under debug mode, launch vminitd as a sub process of pid1\n        // so that we get a chance to collect better logs and errors before pid1 exists\n        // and the kernel panics.\n        #if DEBUG\n        log.info(\"DEBUG mode active, checking FOREGROUND env var\")\n        let environment = ProcessInfo.processInfo.environment\n        let foreground = environment[Self.foregroundEnvVar]\n        log.info(\"checking for shim var \\(Self.foregroundEnvVar)=\\(String(describing: foreground))\")\n\n        if foreground == nil {\n            try Self.runInForeground(log, logLevel: options.logLevel)\n            _exit(0)\n        }\n\n        log.info(\"FOREGROUND is set, running as subprocess, setting subreaper\")\n        // since we are not running as pid1 in this mode we must set ourselves\n        // as a subpreaper so that all child processes are reaped by us and not\n        // passed onto our parent.\n        CZ_set_sub_reaper()\n        #endif\n\n        signal(SIGPIPE, SIG_IGN)\n\n        log.info(\"vminitd booting\")\n\n        // Set of mounts necessary to be mounted prior to taking any RPCs.\n        // 1. /proc as the sysctl rpc wouldn't make sense if it wasn't there (NOTE: This is done before this method\n        // due to Swift seemingly requiring /proc to be present for the async runtime to spin up).\n        // 2. /run as that is where we store container state.\n        // 3. /sys as we need it for /sys/fs/cgroup\n        // 4. /sys/fs/cgroup to add the agent to a cgroup, as well as containers later.\n        let mounts = [\n            ContainerizationOS.Mount(\n                type: \"tmpfs\",\n                source: \"tmpfs\",\n                target: \"/run\",\n                options: []\n            ),\n            ContainerizationOS.Mount(\n                type: \"sysfs\",\n                source: \"sysfs\",\n                target: \"/sys\",\n                options: []\n            ),\n            ContainerizationOS.Mount(\n                type: \"cgroup2\",\n                source: \"none\",\n                target: \"/sys/fs/cgroup\",\n                options: []\n            ),\n        ]\n\n        for mnt in mounts {\n            log.info(\"mounting \\(mnt.target)\")\n\n            try mnt.mount(createWithPerms: 0o755)\n        }\n        try Binfmt.mount()\n\n        let cgManager = Cgroup2Manager(\n            group: URL(filePath: \"/vminitd\"),\n            logger: log\n        )\n        try cgManager.create()\n        try cgManager.toggleAllAvailableControllers(enable: true)\n\n        // Set memory.high threshold to 75 MiB\n        let threshold: UInt64 = 75 * 1024 * 1024\n        try cgManager.setMemoryHigh(bytes: threshold)\n        try cgManager.addProcess(pid: getpid())\n\n        let memoryMonitor = try MemoryMonitor(\n            cgroupManager: cgManager,\n            threshold: threshold,\n            logger: log\n        ) { [log] (currentUsage, highMark) in\n            log.warning(\n                \"vminitd memory threshold exceeded\",\n                metadata: [\n                    \"threshold_bytes\": \"\\(threshold)\",\n                    \"current_bytes\": \"\\(currentUsage)\",\n                    \"high_events_total\": \"\\(highMark)\",\n                ])\n        }\n\n        let t = Thread { [log] in\n            do {\n                try memoryMonitor.run()\n            } catch {\n                log.error(\"memory monitor failed: \\(error)\")\n            }\n        }\n        t.start()\n\n        let eg = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)\n        let blockingPool = NIOThreadPool(numberOfThreads: System.coreCount)\n        blockingPool.start()\n        let server = Initd(log: log, group: eg, blockingPool: blockingPool)\n\n        do {\n            log.info(\"serving vminitd API\")\n            try await server.serve(port: Self.vsockPort)\n            log.info(\"vminitd API returned, syncing filesystems\")\n\n            #if os(Linux)\n            Musl.sync()\n            #endif\n        } catch {\n            log.error(\"vminitd boot error \\(error)\")\n\n            #if os(Linux)\n            Musl.sync()\n            #endif\n\n            _exit(1)\n        }\n    }\n\n    private static func runInForeground(_ log: Logger, logLevel: String) throws {\n        log.info(\"running vminitd under pid1\")\n\n        var command = Command(\"/sbin/vminitd\", arguments: [\"agent\", \"--log-level\", logLevel])\n        command.attrs = .init(setsid: true)\n        command.stdin = .standardInput\n        command.stdout = .standardOutput\n        command.stderr = .standardError\n        command.environment = [\"\\(foregroundEnvVar)=1\"]\n\n        try command.start()\n        let exitCode = try command.wait()\n        log.info(\"child process exited with code: \\(exitCode)\")\n    }\n\n    private static func adjustLimits(_ log: Logger) throws {\n        let nrOpen = try String(contentsOfFile: \"/proc/sys/fs/nr_open\", encoding: .utf8)\n            .trimmingCharacters(in: .whitespacesAndNewlines)\n        guard let max = rlim_t(nrOpen) else {\n            throw POSIXError(.EINVAL)\n        }\n        log.debug(\"setting RLIMIT_NOFILE to \\(max)\")\n        var limits = rlimit(rlim_cur: max, rlim_max: max)\n        guard setrlimit(RLIMIT_NOFILE, &limits) == 0 else {\n            throw POSIXError(.init(rawValue: errno)!)\n        }\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/Application.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ArgumentParser\nimport ContainerizationOS\nimport Foundation\nimport Logging\n\n@main\nstruct Application: AsyncParsableCommand {\n    static let configuration = CommandConfiguration(\n        commandName: \"vminitd\",\n        abstract: \"Virtual machine init daemon\",\n        version: \"0.1.0\",\n        subcommands: [\n            AgentCommand.self,\n            InitCommand.self,\n            PauseCommand.self,\n        ],\n        defaultSubcommand: AgentCommand.self\n    )\n\n    static func main() async throws {\n        // Busybox-style: if invoked as .cz-init, run init mode directly.\n        let invoked = CommandLine.arguments.first?.split(separator: \"/\").last.map(String.init) ?? \"\"\n        if invoked == \".cz-init\" {\n            let args = Array(CommandLine.arguments.dropFirst())\n            var command = try InitCommand.parse(args)\n            try command.run()\n            return\n        }\n\n        // Swift has issues spawning threads if /proc isn't mounted,\n        // so we do this synchronously before any async code runs.\n        try mountProc()\n\n        var command = try parseAsRoot()\n        if let asyncCommand = command as? AsyncParsableCommand {\n            nonisolated(unsafe) var unsafeCommand = asyncCommand\n            try await unsafeCommand.run()\n        } else {\n            try command.run()\n        }\n    }\n\n    private static func mountProc() throws {\n        // Is it already mounted (would only be true in debug builds where we re-exec ourselves)?\n        if isProcMounted() {\n            return\n        }\n\n        let mnt = ContainerizationOS.Mount(\n            type: \"proc\",\n            source: \"proc\",\n            target: \"/proc\",\n            options: []\n        )\n        try mnt.mount(createWithPerms: 0o755)\n    }\n\n    private static func isProcMounted() -> Bool {\n        guard let data = try? String(contentsOfFile: \"/proc/mounts\", encoding: .utf8) else {\n            return false\n        }\n\n        for line in data.split(separator: \"\\n\") {\n            let fields = line.split(separator: \" \")\n            if fields.count >= 2 {\n                let mountPoint = String(fields[1])\n                if mountPoint == \"/proc\" {\n                    return true\n                }\n            }\n        }\n\n        return false\n    }\n}\n\nstruct LogLevelOption: ParsableArguments {\n    @Option(name: .long, help: \"Set the log level (trace, debug, info, notice, warning, error, critical)\")\n    var logLevel: String = \"info\"\n\n    func resolvedLogLevel() -> Logger.Level {\n        switch logLevel.lowercased() {\n        case \"trace\":\n            return .trace\n        case \"debug\":\n            return .debug\n        case \"info\":\n            return .info\n        case \"notice\":\n            return .notice\n        case \"warning\":\n            return .warning\n        case \"error\":\n            return .error\n        case \"critical\":\n            return .critical\n        default:\n            return .info\n        }\n    }\n}\n\nfunc makeLogger(label: String, level: Logger.Level) -> Logger {\n    LoggingSystem.bootstrap(StreamLogHandler.standardError)\n    var log = Logger(label: label)\n    log.logLevel = level\n    return log\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/CommandRunner.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOS\nimport Foundation\nimport Synchronization\n\nstruct ProcessSubscription: Sendable {\n    fileprivate let id: UUID\n}\n\n/// Protocol for running commands and waiting for their exit\nprotocol CommandRunner: Sendable {\n    func start(_ cmd: inout Command) throws -> ProcessSubscription\n    func wait(_ cmd: Command, subscription: ProcessSubscription) async throws -> Int32\n}\n\nstruct DirectCommandRunner: CommandRunner {\n    func start(_ cmd: inout Command) throws -> ProcessSubscription {\n        try cmd.start()\n        return ProcessSubscription(id: UUID())\n    }\n\n    func wait(_ cmd: Command, subscription: ProcessSubscription) async throws -> Int32 {\n        var rus = rusage()\n        var ws = Int32()\n\n        let result = wait4(cmd.pid, &ws, 0, &rus)\n        guard result == cmd.pid else {\n            throw POSIXError(.init(rawValue: errno)!)\n        }\n        return Command.toExitStatus(ws)\n    }\n}\n\nfinal class ReaperCommandRunner: CommandRunner, Sendable {\n    private struct Subscriber {\n        let continuation: AsyncStream<(pid: pid_t, status: Int32)>.Continuation\n        let stream: AsyncStream<(pid: pid_t, status: Int32)>\n    }\n\n    private let subscribers: Mutex<[UUID: Subscriber]> = Mutex([:])\n\n    func start(_ cmd: inout Command) throws -> ProcessSubscription {\n        // Subscribe before starting to avoid missing fast exits\n        let id = UUID()\n        let (stream, continuation) = AsyncStream<(pid: pid_t, status: Int32)>.makeStream()\n\n        subscribers.withLock { subscribers in\n            subscribers[id] = Subscriber(continuation: continuation, stream: stream)\n        }\n\n        try cmd.start()\n\n        return ProcessSubscription(id: id)\n    }\n\n    func wait(_ cmd: Command, subscription: ProcessSubscription) async throws -> Int32 {\n        let pid = cmd.pid\n        let id = subscription.id\n\n        defer {\n            subscribers.withLock { subscribers in\n                subscribers[id]?.continuation.finish()\n                subscribers.removeValue(forKey: id)\n            }\n        }\n\n        // Get the stream from the subscriber\n        guard let stream = subscribers.withLock({ $0[id]?.stream }) else {\n            throw POSIXError(.ECHILD)\n        }\n\n        for await (exitPid, status) in stream {\n            if exitPid == pid {\n                return status\n            }\n        }\n\n        throw POSIXError(.ECHILD)\n    }\n\n    /// Broadcast exit to all subscribers\n    func notifyExit(pid: pid_t, status: Int32) {\n        subscribers.withLock { subscribers in\n            for subscriber in subscribers.values {\n                subscriber.continuation.yield((pid, status))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/ContainerProcess.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOS\nimport Foundation\n\n/// Exit status information for a container process\nstruct ContainerExitStatus: Sendable {\n    var exitCode: Int32\n    var exitedAt: Date\n}\n\n/// Protocol for managing container processes\n///\n/// This protocol abstracts the underlying container runtime implementation,\n/// allowing for different backends like vmexec or runc.\nprotocol ContainerProcess: Sendable {\n    /// Unique identifier for the container process\n    var id: String { get }\n\n    /// Process ID of the running container (nil if not started)\n    var pid: Int32? { get }\n\n    /// Start the container process\n    /// - Returns: The process ID of the started container\n    /// - Throws: If the process fails to start\n    func start() async throws -> Int32\n\n    /// Wait for the container process to exit\n    /// - Returns: Exit status information when the process exits\n    func wait() async -> ContainerExitStatus\n\n    /// Send a signal to the container process\n    /// - Parameter signal: The signal number to send\n    /// - Throws: If the signal cannot be sent\n    func kill(_ signal: Int32) async throws\n\n    /// Resize the terminal for the container process\n    /// - Parameter size: The new terminal size\n    /// - Throws: If the terminal cannot be resized or process doesn't have a terminal\n    func resize(size: Terminal.Size) throws\n\n    /// Close stdin for the container process\n    /// - Throws: If stdin cannot be closed\n    func closeStdin() throws\n\n    /// Delete the container process and clean up resources\n    /// - Throws: If cleanup fails\n    func delete() async throws\n\n    /// Set the exit status of the process.\n    func setExit(_ status: Int32)\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/HostStdio.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nstruct HostStdio: Sendable {\n    let stdin: UInt32?\n    let stdout: UInt32?\n    let stderr: UInt32?\n    let terminal: Bool\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/IOCloser+Extensions.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOS\nimport Foundation\n\nextension Socket: IOCloser {}\n\nextension Terminal: IOCloser {\n    var fileDescriptor: Int32 {\n        self.handle.fileDescriptor\n    }\n}\n\nextension FileHandle: IOCloser {}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/IOCloser.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nprotocol IOCloser: Sendable {\n    var fileDescriptor: Int32 { get }\n\n    func close() throws\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/IOPair.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport ContainerizationOS\nimport Foundation\nimport Logging\nimport Synchronization\n\nfinal class IOPair: Sendable {\n    private let io: Mutex<IO>\n    private let logger: Logger?\n    private let reason: String\n\n    private struct IO {\n        let from: IOCloser\n        let to: IOCloser\n        let buffer: UnsafeMutableBufferPointer<UInt8>\n        var closed: Bool\n\n        func drain() {\n            let readFrom = OSFile(fd: from.fileDescriptor)\n            let writeTo = OSFile(fd: to.fileDescriptor)\n\n            while true {\n                let r = readFrom.read(buffer)\n                if r.read > 0 {\n                    let view = UnsafeMutableBufferPointer(\n                        start: buffer.baseAddress,\n                        count: r.read\n                    )\n\n                    let w = writeTo.write(view)\n                    if w.wrote != r.read {\n                        return\n                    }\n                }\n\n                switch r.action {\n                case .eof, .again, .error(_):\n                    return\n                default:\n                    break\n                }\n            }\n        }\n\n        mutating func close(logger: Logger?) {\n            if self.closed {\n                return\n            }\n\n            // Try and drain IO first.\n            self.drain()\n\n            // Remove the fd from our global epoll instance first.\n            let readFromFd = self.from.fileDescriptor\n            do {\n                try ProcessSupervisor.default.poller.delete(readFromFd)\n            } catch {\n                logger?.error(\"failed to delete fd from epoll \\(readFromFd): \\(error)\")\n            }\n\n            do {\n                try self.from.close()\n            } catch {\n                logger?.error(\"failed to close reader fd for IOPair: \\(error)\")\n            }\n\n            do {\n                try self.to.close()\n            } catch {\n                logger?.error(\"failed to close writer fd for IOPair: \\(error)\")\n            }\n            self.buffer.deallocate()\n            self.closed = true\n        }\n    }\n\n    init(\n        readFrom: IOCloser,\n        writeTo: IOCloser,\n        reason: String,\n        logger: Logger? = nil\n    ) {\n        let buffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: Int(getpagesize()))\n        self.io = Mutex(\n            IO(\n                from: readFrom,\n                to: writeTo,\n                buffer: buffer,\n                closed: false\n            ))\n        self.reason = reason\n        self.logger = logger\n    }\n\n    func relay(ignoreHup: Bool = false) throws {\n        self.logger?.info(\"setting up relay for \\(reason)\")\n\n        let (readFromFd, writeToFd) = self.io.withLock { io in\n            (io.from.fileDescriptor, io.to.fileDescriptor)\n        }\n\n        let readFrom = OSFile(fd: readFromFd)\n        let writeTo = OSFile(fd: writeToFd)\n\n        try ProcessSupervisor.default.poller.add(readFromFd, mask: EPOLLIN) { mask in\n            self.io.withLock { io in\n                if io.closed {\n                    return\n                }\n\n                if mask.isHangup && !mask.readyToRead {\n                    self.logger?.debug(\"received EPOLLHUP with no EPOLLIN\")\n                    if !ignoreHup {\n                        io.close(logger: self.logger)\n                    }\n                    return\n                }\n\n                // Loop so we drain fully.\n                while true {\n                    let r = readFrom.read(io.buffer)\n                    if r.read > 0 {\n                        let view = UnsafeMutableBufferPointer(\n                            start: io.buffer.baseAddress,\n                            count: r.read\n                        )\n\n                        let w = writeTo.write(view)\n                        if w.wrote != r.read {\n                            self.logger?.error(\"stopping relay: short write for stdio\")\n                            io.close(logger: self.logger)\n                            return\n                        }\n                    }\n\n                    switch r.action {\n                    case .error(let errno):\n                        self.logger?.error(\"failed with errno \\(errno) while reading for fd \\(readFromFd)\")\n                        fallthrough\n                    case .eof:\n                        self.logger?.debug(\"closing relay for \\(readFromFd)\")\n                        io.close(logger: self.logger)\n                        return\n                    case .again:\n                        if mask.isHangup && !ignoreHup {\n                            self.logger?.error(\"received EPOLLHUP and EAGAIN exiting\")\n                            self.close()\n                        }\n                        return\n                    default:\n                        break\n                    }\n                }\n            }\n        }\n    }\n\n    func close() {\n        self.io.withLock { io in\n            self.logger?.info(\"closing relay for \\(reason)\")\n            io.close(logger: self.logger)\n        }\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/InitCommand.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ArgumentParser\nimport ContainerizationOS\nimport LCShim\nimport Musl\n\n/// A minimal init process that:\n/// - Spawns and monitors a child process\n/// - Forwards signals to the child\n/// - Reaps zombie processes\n/// - Exits with the child's exit code\nstruct InitCommand: ParsableCommand {\n    static let configuration = CommandConfiguration(\n        commandName: \"init\",\n        abstract: \"Run as a minimal init process\"\n    )\n\n    @Flag(name: .shortAndLong, help: \"Send signals to the child's process group instead of just the child\")\n    var processGroup: Bool = false\n\n    @Argument(help: \"The command to run\")\n    var command: String\n\n    @Argument(parsing: .captureForPassthrough, help: \"Arguments for the command\")\n    var arguments: [String] = []\n\n    /// Signals that should NOT be forwarded to the child.\n    private static let ignoredSignals: Set<Int32> = [\n        SIGCHLD,  // We handle this for zombie reaping\n        SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGABRT, SIGTRAP, SIGSYS,  // Synchronous signals\n    ]\n\n    mutating func run() throws {\n        // If we're not PID 1, register as a child subreaper so orphaned\n        // processes get reparented to us and we can reap them.\n        if getpid() != 1 {\n            CZ_set_sub_reaper()\n        }\n\n        // Block all signals. We'll handle them synchronously via sigtimedwait\n        var allSignals = sigset_t()\n        sigfillset(&allSignals)\n        sigprocmask(SIG_BLOCK, &allSignals, nil)\n\n        let resolvedCommand = Path.lookPath(command)?.path ?? command\n\n        var cmd = Command(resolvedCommand, arguments: arguments)\n        cmd.stdin = .standardInput\n        cmd.stdout = .standardOutput\n        cmd.stderr = .standardError\n\n        cmd.attrs = .init(setPGroup: true, setForegroundPGroup: true, setSignalDefault: true)\n\n        try cmd.start()\n        let childPid = cmd.pid\n        let signalTarget = processGroup ? -childPid : childPid\n        var timeout = timespec(tv_sec: 0, tv_nsec: 100_000_000)\n\n        // Handle signals and reap zombies\n        var childExitStatus: Int32?\n        while childExitStatus == nil {\n            var siginfo = siginfo_t()\n            let sig = sigtimedwait(&allSignals, &siginfo, &timeout)\n\n            if sig > 0 && !Self.ignoredSignals.contains(sig) {\n                _ = Musl.kill(signalTarget, sig)\n            }\n\n            while true {\n                var status: Int32 = 0\n                let pid = waitpid(-1, &status, WNOHANG)\n                if pid <= 0 {\n                    break\n                }\n                if pid == childPid {\n                    childExitStatus = Command.toExitStatus(status)\n                }\n            }\n        }\n\n        Musl.exit(childExitStatus ?? 1)\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/ManagedContainer.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Cgroup\nimport ContainerizationError\nimport ContainerizationOCI\nimport ContainerizationOS\nimport Foundation\nimport Logging\n\nactor ManagedContainer {\n    let id: String\n    let initProcess: any ContainerProcess\n\n    private let cgroupManager: Cgroup2Manager\n    private let log: Logger\n    private let bundle: ContainerizationOCI.Bundle\n    private let needsCgroupCleanup: Bool\n    private var execs: [String: any ContainerProcess] = [:]\n\n    var pid: Int32? {\n        self.initProcess.pid\n    }\n\n    init(\n        id: String,\n        stdio: HostStdio,\n        spec: ContainerizationOCI.Spec,\n        ociRuntimePath: String? = nil,\n        log: Logger\n    ) async throws {\n        var cgroupsPath: String\n        if let cgPath = spec.linux?.cgroupsPath {\n            cgroupsPath = cgPath\n        } else {\n            cgroupsPath = \"/container/\\(id)\"\n        }\n\n        let bundle = try ContainerizationOCI.Bundle.create(\n            path: Self.craftBundlePath(id: id),\n            spec: spec\n        )\n        log.debug(\"created bundle with spec \\(spec)\")\n\n        let cgManager = Cgroup2Manager(\n            group: URL(filePath: cgroupsPath),\n            logger: log\n        )\n        try cgManager.create()\n\n        do {\n            try cgManager.toggleAllAvailableControllers(enable: true)\n\n            let initProcess: any ContainerProcess\n\n            if let runtimePath = ociRuntimePath {\n                // Use runc runtime\n                let runc = ProcessSupervisor.default.getRuncWithReaper(\n                    Runc(\n                        command: runtimePath,\n                        root: \"/run/runc\"\n                    )\n                )\n                initProcess = try RuncProcess(\n                    id: id,\n                    stdio: stdio,\n                    bundle: bundle,\n                    runc: runc,\n                    log: log\n                )\n                self.needsCgroupCleanup = false\n                log.info(\"created runc init process with runtime: \\(runtimePath)\")\n            } else {\n                // Use vmexec runtime\n                initProcess = try ManagedProcess(\n                    id: id,\n                    stdio: stdio,\n                    bundle: bundle,\n                    cgroupManager: cgManager,\n                    owningPid: nil,\n                    log: log\n                )\n                self.needsCgroupCleanup = true\n                log.info(\"created vmexec init process\")\n            }\n\n            self.cgroupManager = cgManager\n            self.initProcess = initProcess\n            self.id = id\n            self.bundle = bundle\n            self.log = log\n        } catch {\n            try? cgManager.delete()\n            throw error\n        }\n    }\n}\n\nextension ManagedContainer {\n    // removeCgroupWithRetry will remove a cgroup path handling EAGAIN and EBUSY errors and\n    // retrying the remove after an exponential timeout\n    private func removeCgroupWithRetry() async throws {\n        var delay = 10  // 10ms\n        let maxRetries = 5\n\n        for i in 0..<maxRetries {\n            if i != 0 {\n                try await Task.sleep(for: .milliseconds(delay))\n                delay *= 2\n            }\n\n            do {\n                try self.cgroupManager.delete(force: true)\n                return\n            } catch let error as Cgroup2Manager.Error {\n                guard case .errno(let errnoValue, let message) = error,\n                    errnoValue == EBUSY || errnoValue == EAGAIN\n                else {\n                    throw error\n                }\n                self.log.warning(\n                    \"cgroup deletion failed with EBUSY/EAGAIN, retrying\",\n                    metadata: [\n                        \"attempt\": \"\\(i + 1)\",\n                        \"delay\": \"\\(delay)\",\n                        \"errno\": \"\\(errnoValue)\",\n                        \"context\": \"\\(message)\",\n                    ])\n                continue\n            }\n        }\n\n        throw ContainerizationError(\n            .internalError,\n            message: \"cgroups: unable to remove cgroup after \\(maxRetries) retries\"\n        )\n    }\n\n    private func ensureExecExists(_ id: String) throws {\n        if self.execs[id] == nil {\n            throw ContainerizationError(\n                .invalidState,\n                message: \"exec \\(id) does not exist in container \\(self.id)\"\n            )\n        }\n    }\n\n    func createExec(\n        id: String,\n        stdio: HostStdio,\n        process: ContainerizationOCI.Process\n    ) throws {\n        log.debug(\"creating exec process with \\(process)\")\n\n        // Write the process config to the bundle, and pass this on\n        // over to ManagedProcess to deal with.\n        try self.bundle.createExecSpec(\n            id: id,\n            process: process\n        )\n        let process = try ManagedProcess(\n            id: id,\n            stdio: stdio,\n            bundle: self.bundle,\n            owningPid: self.initProcess.pid,\n            log: self.log\n        )\n        self.execs[id] = process\n    }\n\n    func start(execID: String) async throws -> Int32 {\n        let proc = try self.getExecOrInit(execID: execID)\n        return try await ProcessSupervisor.default.start(process: proc)\n    }\n\n    func wait(execID: String) async throws -> ContainerExitStatus {\n        let proc = try self.getExecOrInit(execID: execID)\n        return await proc.wait()\n    }\n\n    func kill(execID: String, _ signal: Int32) async throws {\n        let proc = try self.getExecOrInit(execID: execID)\n        try await proc.kill(signal)\n    }\n\n    func resize(execID: String, size: Terminal.Size) throws {\n        let proc = try self.getExecOrInit(execID: execID)\n        try proc.resize(size: size)\n    }\n\n    func closeStdin(execID: String) throws {\n        let proc = try self.getExecOrInit(execID: execID)\n        try proc.closeStdin()\n    }\n\n    func deleteExec(id: String) throws {\n        try ensureExecExists(id)\n        do {\n            try self.bundle.deleteExecSpec(id: id)\n        } catch {\n            self.log.error(\"failed to remove exec spec from filesystem: \\(error)\")\n        }\n        self.execs.removeValue(forKey: id)\n    }\n\n    func delete() async throws {\n        // Delete the init process if it's a RuncProcess\n        try await self.initProcess.delete()\n\n        // Delete the bundle and cgroup\n        try self.bundle.delete()\n        if self.needsCgroupCleanup {\n            try await self.removeCgroupWithRetry()\n        }\n    }\n\n    func stats() throws -> Cgroup2Stats {\n        try self.cgroupManager.stats()\n    }\n\n    func getMemoryEvents() throws -> MemoryEvents {\n        try self.cgroupManager.getMemoryEvents()\n    }\n\n    func getExecOrInit(execID: String) throws -> any ContainerProcess {\n        if execID == self.id {\n            return self.initProcess\n        }\n        guard let proc = self.execs[execID] else {\n            throw ContainerizationError(\n                .invalidState,\n                message: \"exec \\(execID) does not exist in container \\(self.id)\"\n            )\n        }\n        return proc\n    }\n}\n\nextension ContainerizationOCI.Bundle {\n    func createExecSpec(id: String, process: ContainerizationOCI.Process) throws {\n        let specDir = self.path.appending(path: \"execs/\\(id)\")\n\n        let fm = FileManager.default\n        try fm.createDirectory(\n            atPath: specDir.path,\n            withIntermediateDirectories: true\n        )\n\n        let specData = try JSONEncoder().encode(process)\n        let processConfigPath = specDir.appending(path: \"process.json\")\n        try specData.write(to: processConfigPath)\n    }\n\n    func getExecSpecPath(id: String) -> URL {\n        self.path.appending(path: \"execs/\\(id)/process.json\")\n    }\n\n    func deleteExecSpec(id: String) throws {\n        let specDir = self.path.appending(path: \"execs/\\(id)\")\n\n        let fm = FileManager.default\n        try fm.removeItem(at: specDir)\n    }\n}\n\nextension ManagedContainer {\n    static func craftBundlePath(id: String) -> URL {\n        URL(fileURLWithPath: \"/run/container\").appending(path: id)\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/ManagedProcess.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Cgroup\nimport Containerization\nimport ContainerizationError\nimport ContainerizationOCI\nimport ContainerizationOS\nimport Foundation\nimport GRPC\nimport Logging\nimport Synchronization\n\nfinal class ManagedProcess: ContainerProcess, Sendable {\n    // swiftlint: disable type_name\n    protocol IO {\n        func attach(pid: Int32, fd: Int32) throws\n        func start(process: inout Command) throws\n        func resize(size: Terminal.Size) throws\n        func close() throws\n        func closeStdin() throws\n        func closeAfterExec() throws\n    }\n    // swiftlint: enable type_name\n\n    private struct State {\n        init(io: IO) {\n            self.io = io\n        }\n\n        let io: IO\n        var waiters: [CheckedContinuation<ContainerExitStatus, Never>] = []\n        var exitStatus: ContainerExitStatus? = nil\n        var pid: Int32?\n    }\n\n    private static let ackPid = \"AckPid\"\n    private static let ackConsole = \"AckConsole\"\n\n    let id: String\n\n    private let log: Logger\n    private let command: Command\n    private let state: Mutex<State>\n    private let owningPid: Int32?\n    private let ackPipe: Pipe\n    private let syncPipe: Pipe\n    private let errorPipe: Pipe\n    private let terminal: Bool\n    private let bundle: ContainerizationOCI.Bundle\n    private let cgroupManager: Cgroup2Manager?\n\n    var pid: Int32? {\n        self.state.withLock {\n            $0.pid\n        }\n    }\n\n    init(\n        id: String,\n        stdio: HostStdio,\n        bundle: ContainerizationOCI.Bundle,\n        cgroupManager: Cgroup2Manager? = nil,\n        owningPid: Int32? = nil,\n        log: Logger\n    ) throws {\n        self.id = id\n        var log = log\n        log[metadataKey: \"id\"] = \"\\(id)\"\n        self.log = log\n        self.owningPid = owningPid\n\n        let syncPipe = Pipe()\n        try syncPipe.setCloexec()\n        self.syncPipe = syncPipe\n\n        let ackPipe = Pipe()\n        try ackPipe.setCloexec()\n        self.ackPipe = ackPipe\n\n        let errorPipe = Pipe()\n        try errorPipe.setCloexec()\n        self.errorPipe = errorPipe\n\n        let args: [String]\n        if let owningPid {\n            args = [\n                \"exec\",\n                \"--parent-pid\",\n                \"\\(owningPid)\",\n                \"--process-path\",\n                bundle.getExecSpecPath(id: id).path,\n            ]\n        } else {\n            args = [\"run\", \"--bundle-path\", bundle.path.path]\n        }\n\n        var command = Command(\n            \"/sbin/vmexec\",\n            arguments: args,\n            extraFiles: [\n                syncPipe.fileHandleForWriting,\n                ackPipe.fileHandleForReading,\n                errorPipe.fileHandleForWriting,\n            ]\n        )\n\n        var io: IO\n        if stdio.terminal {\n            log.info(\"setting up terminal I/O\")\n            let attrs = Command.Attrs(setsid: false, setctty: false)\n            command.attrs = attrs\n            io = try TerminalIO(\n                stdio: stdio,\n                log: log\n            )\n        } else {\n            command.attrs = .init(setsid: false)\n            io = StandardIO(\n                stdio: stdio,\n                log: log\n            )\n        }\n\n        log.info(\"starting I/O\")\n\n        // Setup IO early. We expect the host to be listening already.\n        try io.start(process: &command)\n\n        self.cgroupManager = cgroupManager\n        self.command = command\n        self.terminal = stdio.terminal\n        self.bundle = bundle\n        self.state = Mutex(State(io: io))\n    }\n}\n\nextension ManagedProcess {\n    func start() async throws -> Int32 {\n        do {\n            return try self.state.withLock {\n                log.info(\n                    \"starting managed process\",\n                    metadata: [\n                        \"id\": \"\\(id)\"\n                    ])\n\n                // Start the underlying process.\n                try command.start()\n\n                defer {\n                    try? self.ackPipe.fileHandleForWriting.close()\n                    try? self.syncPipe.fileHandleForReading.close()\n                    try? self.ackPipe.fileHandleForReading.close()\n                    try? self.syncPipe.fileHandleForWriting.close()\n                    try? self.errorPipe.fileHandleForWriting.close()\n                }\n\n                // Close our side of any pipes.\n                try $0.io.closeAfterExec()\n                try self.ackPipe.fileHandleForReading.close()\n                try self.syncPipe.fileHandleForWriting.close()\n                try self.errorPipe.fileHandleForWriting.close()\n\n                let size = MemoryLayout<Int32>.size\n                guard let piddata = try syncPipe.fileHandleForReading.read(upToCount: size) else {\n                    throw ContainerizationError(.internalError, message: \"no PID data from sync pipe\")\n                }\n\n                guard piddata.count == size else {\n                    throw ContainerizationError(.internalError, message: \"invalid payload\")\n                }\n\n                let pid = piddata.withUnsafeBytes { ptr in\n                    ptr.load(as: Int32.self)\n                }\n\n                log.info(\n                    \"got back pid data\",\n                    metadata: [\n                        \"pid\": \"\\(pid)\"\n                    ])\n                $0.pid = pid\n\n                // This should probably happen in vmexec, but we don't need to set any cgroup\n                // toggles so the problem is much simpler to just do it here.\n                if let owningPid {\n                    let cgManager = try Cgroup2Manager.loadFromPid(pid: owningPid)\n                    try cgManager.addProcess(pid: pid)\n                }\n\n                log.info(\n                    \"sending pid acknowledgement\",\n                    metadata: [\n                        \"pid\": \"\\(pid)\"\n                    ])\n                try self.ackPipe.fileHandleForWriting.write(contentsOf: Self.ackPid.data(using: .utf8)!)\n\n                if self.terminal {\n                    log.info(\n                        \"wait for PTY FD\",\n                        metadata: [\n                            \"id\": \"\\(id)\"\n                        ])\n\n                    // Wait for a new write that will contain the pty fd if we asked for one.\n                    guard let ptyFd = try self.syncPipe.fileHandleForReading.read(upToCount: size) else {\n                        throw ContainerizationError(\n                            .internalError,\n                            message: \"no PTY data from sync pipe\"\n                        )\n                    }\n                    let fd = ptyFd.withUnsafeBytes { ptr in\n                        ptr.load(as: Int32.self)\n                    }\n                    log.info(\n                        \"received PTY FD from container, attaching\",\n                        metadata: [\n                            \"id\": \"\\(id)\"\n                        ])\n\n                    try $0.io.attach(pid: pid, fd: fd)\n                    try self.ackPipe.fileHandleForWriting.write(contentsOf: Self.ackConsole.data(using: .utf8)!)\n                }\n\n                // Wait for the errorPipe to close (after exec).\n                if let errorData = try? self.errorPipe.fileHandleForReading.readToEnd(),\n                    let errorString = String(data: errorData, encoding: .utf8),\n                    !errorString.isEmpty\n                {\n                    throw ContainerizationError(\n                        .internalError,\n                        message: \"vmexec error: \\(errorString.trimmingCharacters(in: .whitespacesAndNewlines))\"\n                    )\n                }\n\n                log.info(\n                    \"started managed process\",\n                    metadata: [\n                        \"pid\": \"\\(pid)\",\n                        \"id\": \"\\(id)\",\n                    ])\n\n                return pid\n            }\n        } catch {\n            if let errorData = try? self.errorPipe.fileHandleForReading.readToEnd(),\n                let errorString = String(data: errorData, encoding: .utf8),\n                !errorString.isEmpty\n            {\n                throw ContainerizationError(\n                    .internalError,\n                    message: \"vmexec error: \\(errorString.trimmingCharacters(in: .whitespacesAndNewlines))\",\n                    cause: error\n                )\n            }\n            throw error\n        }\n    }\n\n    func setExit(_ status: Int32) {\n        self.state.withLock { state in\n            self.log.info(\n                \"managed process exit\",\n                metadata: [\n                    \"status\": \"\\(status)\"\n                ])\n\n            let exitStatus = ContainerExitStatus(exitCode: status, exitedAt: Date.now)\n            state.exitStatus = exitStatus\n\n            do {\n                try state.io.close()\n            } catch {\n                self.log.error(\"failed to close I/O for process: \\(error)\")\n            }\n\n            for waiter in state.waiters {\n                waiter.resume(returning: exitStatus)\n            }\n\n            self.log.debug(\"\\(state.waiters.count) managed process waiters signaled\")\n            state.waiters.removeAll()\n        }\n    }\n\n    /// Wait on the process to exit\n    func wait() async -> ContainerExitStatus {\n        await withCheckedContinuation { cont in\n            self.state.withLock {\n                if let status = $0.exitStatus {\n                    cont.resume(returning: status)\n                    return\n                }\n                $0.waiters.append(cont)\n            }\n        }\n    }\n\n    func kill(_ signal: Int32) async throws {\n        try self.state.withLock {\n            guard let pid = $0.pid else {\n                throw ContainerizationError(.invalidState, message: \"process PID is required\")\n            }\n\n            guard $0.exitStatus == nil else {\n                return\n            }\n\n            self.log.info(\"sending signal \\(signal) to process \\(pid)\")\n            guard Foundation.kill(pid, signal) == 0 else {\n                throw POSIXError.fromErrno()\n            }\n        }\n    }\n\n    func resize(size: Terminal.Size) throws {\n        try self.state.withLock {\n            guard $0.exitStatus == nil else {\n                return\n            }\n            try $0.io.resize(size: size)\n        }\n    }\n\n    func closeStdin() throws {\n        let io = self.state.withLock { $0.io }\n        try io.closeStdin()\n    }\n\n    func delete() async throws {\n        // vmexec doesn't require explicit cleanup - the process is cleaned up\n        // when it exits and IO is closed via setExit()\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/MemoryMonitor.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(Linux)\n\nimport Cgroup\nimport Foundation\nimport Logging\n\n#if canImport(Musl)\nimport Musl\n#elseif canImport(Glibc)\nimport Glibc\n#endif\n\npackage final class MemoryMonitor: Sendable {\n    private static let inotifyEventSize = 0x10\n\n    private let cgroupManager: Cgroup2Manager\n    private let threshold: UInt64\n    private let logger: Logger\n    private let inotifyFd: Int32\n    private let watchDescriptor: Int32\n    private let onThresholdExceeded: @Sendable (UInt64, UInt64) -> Void\n\n    package init(\n        cgroupManager: Cgroup2Manager,\n        threshold: UInt64,\n        logger: Logger,\n        onThresholdExceeded: @escaping @Sendable (UInt64, UInt64) -> Void\n    ) throws {\n        self.cgroupManager = cgroupManager\n        self.threshold = threshold\n        self.logger = logger\n        self.onThresholdExceeded = onThresholdExceeded\n\n        let fd = inotify_init()\n        guard fd != -1 else {\n            throw Error.inotifyInit(errno: errno)\n        }\n        self.inotifyFd = fd\n\n        let eventsPath = cgroupManager.getMemoryEventsPath()\n        let wd = inotify_add_watch(\n            inotifyFd,\n            eventsPath,\n            UInt32(IN_MODIFY)\n        )\n        guard wd != -1 else {\n            close(fd)\n            throw Error.inotifyAddWatch(errno: errno, path: eventsPath)\n        }\n        self.watchDescriptor = wd\n    }\n\n    /// Run the monitoring loop. Call this from a dedicated thread.\n    /// This function blocks until an error occurs.\n    package func run() throws {\n        let eventsPath = cgroupManager.getMemoryEventsPath()\n\n        logger.info(\n            \"Started memory monitoring\",\n            metadata: [\n                \"threshold_bytes\": \"\\(threshold)\",\n                \"events_path\": \"\\(eventsPath)\",\n            ])\n\n        // Read initial state\n        var highCountMax: UInt64 = 0\n        do {\n            let events = try cgroupManager.getMemoryEvents()\n            highCountMax = events.high\n        } catch {\n            throw Error.readMemoryEvents(error: error)\n        }\n\n        let bufSize = Self.inotifyEventSize * 10\n        var buffer = [UInt8](repeating: 0, count: bufSize)\n        while true {\n            let bytesRead = buffer.withUnsafeMutableBytes { ptr in\n                read(inotifyFd, ptr.baseAddress!, bufSize)\n            }\n\n            if bytesRead < 0 {\n                if errno == EINTR {\n                    continue\n                }\n                throw Error.readFailed(errno: errno)\n            }\n\n            do {\n                let events = try cgroupManager.getMemoryEvents()\n\n                if events.high > highCountMax {\n                    highCountMax = events.high\n\n                    let stats = try cgroupManager.stats()\n                    let currentUsage = stats.memory?.usage ?? 0\n\n                    onThresholdExceeded(currentUsage, events.high)\n                }\n\n                if events.oom > 0 || events.oomKill > 0 {\n                    logger.error(\n                        \"OOM events detected\",\n                        metadata: [\n                            \"oom_events\": \"\\(events.oom)\",\n                            \"oom_kill_events\": \"\\(events.oomKill)\",\n                        ])\n                }\n            } catch {\n                throw Error.readMemoryEvents(error: error)\n            }\n        }\n    }\n\n    deinit {\n        inotify_rm_watch(inotifyFd, watchDescriptor)\n        close(inotifyFd)\n    }\n}\n\nextension MemoryMonitor {\n    package enum Error: Swift.Error, CustomStringConvertible {\n        case inotifyInit(errno: Int32)\n        case inotifyAddWatch(errno: Int32, path: String)\n        case readFailed(errno: Int32)\n        case readMemoryEvents(error: Swift.Error)\n\n        package var description: String {\n            switch self {\n            case .inotifyInit(let errno):\n                return \"failed to initialize inotify: errno \\(errno)\"\n            case .inotifyAddWatch(let errno, let path):\n                return \"failed to add inotify watch on \\(path): errno \\(errno)\"\n            case .readFailed(let errno):\n                return \"failed to read inotify events: errno \\(errno)\"\n            case .readMemoryEvents(let error):\n                return \"failed to read memory events: \\(error)\"\n            }\n        }\n    }\n}\n\n#endif\n"
  },
  {
    "path": "vminitd/Sources/vminitd/OSFile+Splice.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\nextension OSFile {\n    struct SpliceFile: Sendable {\n        fileprivate var file: OSFile\n        fileprivate var offset: Int\n        fileprivate let pipe = Pipe()\n\n        var fileDescriptor: Int32 {\n            file.fileDescriptor\n        }\n\n        var reader: Int32 {\n            pipe.fileHandleForReading.fileDescriptor\n        }\n\n        var writer: Int32 {\n            pipe.fileHandleForWriting.fileDescriptor\n        }\n\n        init(fd: Int32) {\n            self.file = OSFile(fd: fd)\n            self.offset = 0\n        }\n\n        init(handle: FileHandle) {\n            self.file = OSFile(handle: handle)\n            self.offset = 0\n        }\n\n        init(from: OSFile, withOffset: Int = 0) {\n            self.file = from\n            self.offset = withOffset\n        }\n\n        func close() throws {\n            try self.file.close()\n        }\n    }\n\n    static func splice(from: inout SpliceFile, to: inout SpliceFile, count: Int = 1 << 16) throws -> (read: Int, wrote: Int, action: IOAction) {\n        let fromOffset = from.offset\n        let toOffset = to.offset\n\n        while true {\n            while (from.offset - to.offset) < count {\n                let toRead = count - (from.offset - to.offset)\n                let bytesRead = Foundation.splice(from.fileDescriptor, nil, to.writer, nil, toRead, UInt32(bitPattern: SPLICE_F_MOVE | SPLICE_F_NONBLOCK))\n                if bytesRead == -1 {\n                    if errno != EAGAIN && errno != EIO {\n                        throw POSIXError(.init(rawValue: errno)!)\n                    }\n                    break\n                }\n                if bytesRead == 0 {\n                    return (0, 0, .eof)\n                }\n                from.offset += bytesRead\n                if bytesRead < toRead {\n                    break\n                }\n            }\n            if from.offset == to.offset {\n                return (from.offset - fromOffset, to.offset - toOffset, .success)\n            }\n            while to.offset < from.offset {\n                let toWrite = from.offset - to.offset\n                let bytesWrote = Foundation.splice(to.reader, nil, to.fileDescriptor, nil, toWrite, UInt32(bitPattern: SPLICE_F_MOVE | SPLICE_F_NONBLOCK))\n                if bytesWrote == -1 {\n                    if errno != EAGAIN && errno != EIO {\n                        throw POSIXError(.init(rawValue: errno)!)\n                    }\n                    break\n                }\n                to.offset += bytesWrote\n                if bytesWrote == 0 {\n                    return (from.offset - fromOffset, to.offset - toOffset, .brokenPipe)\n                }\n                if bytesWrote < toWrite {\n                    break\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/OSFile.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Foundation\n\nstruct OSFile: Sendable {\n    enum IOAction: Equatable {\n        case eof\n        case again\n        case success\n        case brokenPipe\n        case error(_ errno: Int32)\n    }\n\n    private let fd: Int32\n\n    var closed: Bool {\n        Foundation.fcntl(fd, F_GETFD) == -1 && errno == EBADF\n    }\n\n    var fileDescriptor: Int32 { fd }\n\n    init(fd: Int32) {\n        self.fd = fd\n    }\n\n    init(handle: FileHandle) {\n        self.fd = handle.fileDescriptor\n    }\n\n    func close() throws {\n        guard Foundation.close(self.fd) == 0 else {\n            throw POSIXError(.init(rawValue: errno)!)\n        }\n    }\n\n    func read(_ buffer: UnsafeMutableBufferPointer<UInt8>) -> (read: Int, action: IOAction) {\n        if buffer.count == 0 {\n            return (0, .success)\n        }\n\n        var bytesRead: Int = 0\n        while true {\n            let n = Foundation.read(\n                self.fd,\n                buffer.baseAddress!.advanced(by: bytesRead),\n                buffer.count - bytesRead\n            )\n            if n == -1 {\n                if errno == EAGAIN || errno == EIO {\n                    return (bytesRead, .again)\n                }\n                return (bytesRead, .error(errno))\n            }\n\n            if n == 0 {\n                return (bytesRead, .eof)\n            }\n\n            bytesRead += n\n            if bytesRead < buffer.count {\n                continue\n            }\n            return (bytesRead, .success)\n        }\n    }\n\n    func write(_ buffer: UnsafeMutableBufferPointer<UInt8>) -> (wrote: Int, action: IOAction) {\n        if buffer.count == 0 {\n            return (0, .success)\n        }\n\n        var bytesWrote: Int = 0\n        while true {\n            let n = Foundation.write(\n                self.fd,\n                buffer.baseAddress!.advanced(by: bytesWrote),\n                buffer.count - bytesWrote\n            )\n            if n == -1 {\n                if errno == EAGAIN || errno == EIO {\n                    return (bytesWrote, .again)\n                }\n                return (bytesWrote, .error(errno))\n            }\n\n            if n == 0 {\n                return (bytesWrote, .brokenPipe)\n            }\n\n            bytesWrote += n\n            if bytesWrote < buffer.count {\n                continue\n            }\n            return (bytesWrote, .success)\n        }\n    }\n\n    static func pipe() -> (read: Self, write: Self) {\n        let pipe = Pipe()\n        return (Self(handle: pipe.fileHandleForReading), Self(handle: pipe.fileHandleForWriting))\n    }\n\n    static func open(path: String) throws -> Self {\n        try open(path: path, mode: O_RDONLY | O_CLOEXEC)\n    }\n\n    static func open(path: String, mode: Int32) throws -> Self {\n        let fd = Foundation.open(path, mode)\n        if fd < 0 {\n            throw POSIXError(.init(rawValue: errno)!)\n        }\n        return Self(fd: fd)\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/PauseCommand.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ArgumentParser\nimport Dispatch\nimport Logging\nimport Musl\n\nstruct PauseCommand: ParsableCommand {\n    static let configuration = CommandConfiguration(\n        commandName: \"pause\",\n        abstract: \"Run the pause container\"\n    )\n\n    @OptionGroup var options: LogLevelOption\n\n    mutating func run() throws {\n        let log = makeLogger(label: \"pause\", level: options.resolvedLogLevel())\n\n        if getpid() != 1 {\n            log.warning(\"pause should be the first process\")\n        }\n\n        // NOTE: For whatever reason, using signal() for the below causes a swift compiler issue.\n        // Can revert whenever that is understood.\n        let sigintSource = DispatchSource.makeSignalSource(signal: SIGINT)\n        sigintSource.setEventHandler {\n            log.info(\"Shutting down, got SIGINT\")\n            Musl.exit(0)\n        }\n        sigintSource.resume()\n\n        let sigtermSource = DispatchSource.makeSignalSource(signal: SIGTERM)\n        sigtermSource.setEventHandler {\n            log.info(\"Shutting down, got SIGTERM\")\n            Musl.exit(0)\n        }\n        sigtermSource.resume()\n\n        let sigchldSource = DispatchSource.makeSignalSource(signal: SIGCHLD)\n        sigchldSource.setEventHandler {\n            var status: Int32 = 0\n            while waitpid(-1, &status, WNOHANG) > 0 {}\n        }\n        sigchldSource.resume()\n\n        log.info(\"pause container running, waiting for signals...\")\n\n        while true {\n            Musl.pause()\n        }\n\n        log.error(\"Error: infinite loop terminated\")\n        Musl.exit(42)\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/ProcessSupervisor.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOS\nimport Foundation\nimport Logging\nimport Synchronization\n\nfinal class ProcessSupervisor: Sendable {\n    let poller: Epoll\n\n    private let queue: DispatchQueue\n    // `DispatchSourceSignal` is thread-safe.\n    private nonisolated(unsafe) let source: DispatchSourceSignal\n\n    private struct State {\n        var processes: [any ContainerProcess] = []\n        var log: Logger?\n    }\n\n    private let state: Mutex<State>\n    private let reaperCommandRunner = ReaperCommandRunner()\n\n    func setLog(_ log: Logger?) {\n        self.state.withLock { $0.log = log }\n    }\n\n    static let `default` = ProcessSupervisor()\n\n    private init() {\n        let queue = DispatchQueue(label: \"process-supervisor\")\n        self.source = DispatchSource.makeSignalSource(signal: SIGCHLD, queue: queue)\n        self.queue = queue\n        self.poller = try! Epoll()\n        self.state = Mutex(State())\n        let t = Thread {\n            try! self.poller.run()\n        }\n        t.start()\n    }\n\n    func ready() {\n        self.source.setEventHandler {\n            self.handleSignal()\n        }\n        self.source.resume()\n    }\n\n    private func handleSignal() {\n        dispatchPrecondition(condition: .onQueue(queue))\n\n        let exited = Reaper.reap()\n\n        for (pid, status) in exited {\n            reaperCommandRunner.notifyExit(pid: pid, status: status)\n        }\n\n        self.state.withLock { state in\n            state.log?.debug(\"received SIGCHLD, reaping processes\")\n            state.log?.debug(\"finished wait4 of \\(exited.count) processes\")\n            state.log?.debug(\"checking for exit of managed process\", metadata: [\"exits\": \"\\(exited)\", \"processes\": \"\\(state.processes.count)\"])\n\n            let exitedProcesses = state.processes.filter { proc in\n                exited.contains { pid, _ in\n                    proc.pid == pid\n                }\n            }\n\n            for proc in exitedProcesses {\n                guard let pid = proc.pid else {\n                    continue\n                }\n\n                if let status = exited[pid] {\n                    state.log?.debug(\n                        \"managed process exited\",\n                        metadata: [\n                            \"pid\": \"\\(pid)\",\n                            \"status\": \"\\(status)\",\n                            \"count\": \"\\(state.processes.count - 1)\",\n                        ])\n                    proc.setExit(status)\n                    state.processes.removeAll(where: { $0.pid == pid })\n                }\n            }\n        }\n    }\n\n    func start(process: any ContainerProcess) async throws -> Int32 {\n        self.state.withLock { state in\n            state.log?.debug(\"in supervisor lock to start process\")\n            state.processes.append(process)\n        }\n        do {\n            return try await process.start()\n        } catch {\n            self.state.withLock { state in\n                state.processes.removeAll(where: { $0.id == process.id })\n            }\n            throw error\n        }\n    }\n\n    /// Get a Runc instance configured with the reaper command runner\n    func getRuncWithReaper(_ base: Runc = Runc()) -> Runc {\n        var runc = base\n        runc.commandRunner = reaperCommandRunner\n        return runc\n    }\n\n    deinit {\n        source.cancel()\n        try? poller.shutdown()\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/Runc/ConsoleSocket.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOS\nimport Foundation\n\n#if os(Linux)\n\n/// A Unix socket for receiving PTY master file descriptors from runc\npublic final class ConsoleSocket: Sendable {\n    private let socket: Socket\n    private let socketPath: String\n\n    /// The path to the console socket\n    public var path: String { socketPath }\n\n    /// Create a new console socket at the specified path\n    public init(path: String) throws {\n        let absPath = path.starts(with: \"/\") ? path : FileManager.default.currentDirectoryPath + \"/\" + path\n        self.socketPath = absPath\n\n        let pathURL = URL(fileURLWithPath: absPath)\n        let dir = pathURL.deletingLastPathComponent().path\n        try FileManager.default.createDirectory(\n            atPath: dir,\n            withIntermediateDirectories: true,\n            attributes: nil\n        )\n\n        let socketType = try UnixType(path: absPath, unlinkExisting: true)\n        self.socket = try Socket(type: socketType)\n\n        try socket.listen()\n    }\n\n    /// Create a temporary console socket in the runtime directory\n    public static func temporary() throws -> ConsoleSocket {\n        let tmpDir = \"/tmp\"\n        let socketDir = tmpDir + \"/runc-console-\\(UUID().uuidString)\"\n        let socketPath = socketDir + \"/console.sock\"\n\n        try FileManager.default.createDirectory(\n            atPath: socketDir,\n            withIntermediateDirectories: true,\n            attributes: nil\n        )\n\n        let socket = try ConsoleSocket(path: socketPath)\n        return socket\n    }\n\n    /// Receive the PTY master file descriptor from runc\n    public func receiveMaster() throws -> Int32 {\n        let connection = try socket.accept()\n        defer { try? connection.close() }\n        return try connection.receiveFileDescriptor()\n    }\n\n    /// Close the socket and optionally remove the socket file\n    public func close() throws {\n        try socket.close()\n        try FileManager.default.removeItem(atPath: socketPath)\n    }\n\n    deinit {\n        try? close()\n    }\n}\n\n#endif  // os(Linux)\n"
  },
  {
    "path": "vminitd/Sources/vminitd/Runc/Runc.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOCI\nimport ContainerizationOS\nimport Foundation\n\n/// Log format for runc output\nenum LogFormat: String, Sendable {\n    case json\n    case text\n}\n\n/// Configuration and client for interacting with the runc binary\nstruct Runc: Sendable {\n    /// IO configuration for runc operations\n    struct IO: Sendable {\n        var stdin: FileHandle?\n        var stdout: FileHandle?\n        var stderr: FileHandle?\n\n        init(\n            stdin: FileHandle? = nil,\n            stdout: FileHandle? = nil,\n            stderr: FileHandle? = nil\n        ) {\n            self.stdin = stdin\n            self.stdout = stdout\n            self.stderr = stderr\n        }\n\n        static let inherit = IO()\n    }\n\n    /// Path to the runc binary\n    var command: String\n\n    /// Root directory for container state\n    var root: String?\n\n    /// Enable debug output\n    var debug: Bool\n\n    /// Path to log file\n    var log: String?\n\n    /// Format for log output\n    var logFormat: LogFormat?\n\n    /// Signal to send when parent process dies\n    var pdeathSignal: Int32?\n\n    /// Set process group ID\n    var setpgid: Bool\n\n    /// Path to criu binary for checkpoint/restore\n    var criu: String?\n\n    /// Use systemd cgroup manager\n    var systemdCgroup: Bool\n\n    /// Enable rootless mode\n    var rootless: Bool\n\n    /// Additional arguments to pass to runc\n    var extraArgs: [String]\n\n    /// Command runner to use instead of direct wait4 (for PID 1 environments with reapers)\n    var commandRunner: (any CommandRunner)?\n\n    init(\n        command: String = \"runc\",\n        root: String? = nil,\n        debug: Bool = false,\n        log: String? = nil,\n        logFormat: LogFormat? = nil,\n        pdeathSignal: Int32? = nil,\n        setpgid: Bool = false,\n        criu: String? = nil,\n        systemdCgroup: Bool = false,\n        rootless: Bool = false,\n        extraArgs: [String] = [],\n        commandRunner: (any CommandRunner)? = nil\n    ) {\n        self.command = command\n        self.root = root\n        self.debug = debug\n        self.log = log\n        self.logFormat = logFormat\n        self.pdeathSignal = pdeathSignal\n        self.setpgid = setpgid\n        self.criu = criu\n        self.systemdCgroup = systemdCgroup\n        self.rootless = rootless\n        self.extraArgs = extraArgs\n        self.commandRunner = commandRunner\n    }\n}\n\n/// Options for creating a container\nstruct CreateOpts: Sendable {\n    /// Path to file to write container PID\n    var pidFile: String?\n\n    /// Path to console socket for terminal access\n    var consoleSocket: String?\n\n    /// Detach from the container process\n    var detach: Bool\n\n    /// Do not use pivot_root to change root\n    var noPivot: Bool\n\n    /// Do not create a new session\n    var noNewKeyring: Bool\n\n    /// Additional file descriptors to pass to the container\n    var extraFiles: [FileHandle]\n\n    /// IO configuration for the runc process\n    var io: Runc.IO\n\n    init(\n        pidFile: String? = nil,\n        consoleSocket: String? = nil,\n        detach: Bool = false,\n        noPivot: Bool = false,\n        noNewKeyring: Bool = false,\n        extraFiles: [FileHandle] = [],\n        io: Runc.IO = .inherit\n    ) {\n        self.pidFile = pidFile\n        self.consoleSocket = consoleSocket\n        self.detach = detach\n        self.noPivot = noPivot\n        self.noNewKeyring = noNewKeyring\n        self.extraFiles = extraFiles\n        self.io = io\n    }\n}\n\n/// Options for executing a process in a container\nstruct ExecOpts: Sendable {\n    /// Path to file to write process PID\n    var pidFile: String?\n\n    /// Path to console socket for terminal access\n    var consoleSocket: String?\n\n    /// Detach from the process\n    var detach: Bool\n\n    /// Path to process.json file\n    var processPath: String?\n\n    /// IO configuration for the runc process\n    var io: Runc.IO\n\n    init(\n        pidFile: String? = nil,\n        consoleSocket: String? = nil,\n        detach: Bool = false,\n        processPath: String? = nil,\n        io: Runc.IO = .inherit\n    ) {\n        self.pidFile = pidFile\n        self.consoleSocket = consoleSocket\n        self.detach = detach\n        self.processPath = processPath\n        self.io = io\n    }\n}\n\n/// Options for deleting a container\nstruct DeleteOpts: Sendable {\n    /// Force deletion of a running container\n    var force: Bool\n\n    init(force: Bool = false) {\n        self.force = force\n    }\n}\n\n/// Options for restoring a container from checkpoint\nstruct RestoreOpts: Sendable {\n    /// Path to file to write container PID\n    var pidFile: String?\n\n    /// Path to console socket for terminal access\n    var consoleSocket: String?\n\n    /// Detach from the container process\n    var detach: Bool\n\n    /// Do not use pivot_root to change root\n    var noPivot: Bool\n\n    /// Do not create a new session\n    var noNewKeyring: Bool\n\n    /// Path to checkpoint image\n    var imagePath: String?\n\n    /// Path to parent checkpoint\n    var parentPath: String?\n\n    /// Work directory for CRIU\n    var workPath: String?\n\n    init(\n        pidFile: String? = nil,\n        consoleSocket: String? = nil,\n        detach: Bool = false,\n        noPivot: Bool = false,\n        noNewKeyring: Bool = false,\n        imagePath: String? = nil,\n        parentPath: String? = nil,\n        workPath: String? = nil\n    ) {\n        self.pidFile = pidFile\n        self.consoleSocket = consoleSocket\n        self.detach = detach\n        self.noPivot = noPivot\n        self.noNewKeyring = noNewKeyring\n        self.imagePath = imagePath\n        self.parentPath = parentPath\n        self.workPath = workPath\n    }\n}\n\n/// Container information returned from list operation\nstruct Container: Sendable, Codable {\n    let id: String\n    let pid: Int\n    let status: String\n    let bundle: String\n    let rootfs: String\n    let created: Date\n    let annotations: [String: String]?\n\n    enum CodingKeys: String, CodingKey {\n        case id\n        case pid\n        case status\n        case bundle\n        case rootfs\n        case created\n        case annotations\n    }\n}\n\nextension Runc {\n    enum Error: Swift.Error, CustomStringConvertible {\n        case invalidJSON(String)\n        case commandFailed(Int32, String)\n        case invalidPidFile(String)\n\n        var description: String {\n            switch self {\n            case .invalidJSON(let detail):\n                return \"invalid JSON: \\(detail)\"\n            case .commandFailed(let status, let output):\n                return \"command failed with status \\(status): \\(output)\"\n            case .invalidPidFile(let path):\n                return \"invalid or missing PID file: \\(path)\"\n            }\n        }\n    }\n}\n\n// MARK: - Command Building and Execution\n\nextension Runc {\n    /// Build base arguments for runc command\n    func baseArgs() -> [String] {\n        var args: [String] = []\n\n        if let root = root {\n            args += [\"--root\", root]\n        }\n\n        if debug {\n            args.append(\"--debug\")\n        }\n\n        if let log = log {\n            args += [\"--log\", log]\n        }\n\n        if let logFormat = logFormat {\n            args += [\"--log-format\", logFormat.rawValue]\n        }\n\n        if systemdCgroup {\n            args.append(\"--systemd-cgroup\")\n        }\n\n        if rootless {\n            args.append(\"--rootless\")\n        }\n\n        args += extraArgs\n\n        return args\n    }\n\n    /// Execute a runc command and return the output\n    func execute(\n        args: [String],\n        stdin: FileHandle? = nil,\n        stdout: FileHandle? = nil,\n        stderr: FileHandle? = nil,\n        extraFiles: [FileHandle] = [],\n        directory: String? = nil\n    ) async throws -> (status: Int32, output: Data) {\n        var cmd = Command(\n            command,\n            arguments: args,\n            directory: directory,\n            extraFiles: extraFiles\n        )\n\n        // Setup IO\n        let outPipe = Pipe()\n        cmd.stdin = stdin\n        cmd.stdout = stdout ?? outPipe.fileHandleForWriting\n        cmd.stderr = stderr ?? outPipe.fileHandleForWriting\n\n        if let pdeathSignal = pdeathSignal {\n            cmd.attrs.pdeathSignal = pdeathSignal\n        }\n\n        if setpgid {\n            cmd.attrs.setPGroup = true\n        }\n\n        let exitStatus: Int32\n\n        if let runner = commandRunner {\n            let subscription = try runner.start(&cmd)\n            exitStatus = try await runner.wait(cmd, subscription: subscription)\n        } else {\n            try cmd.start()\n            exitStatus = try cmd.wait()\n        }\n\n        var output = Data()\n        if stdout == nil {\n            try? outPipe.fileHandleForWriting.close()\n            output = try outPipe.fileHandleForReading.readToEnd() ?? Data()\n        }\n\n        return (exitStatus, output)\n    }\n\n    /// Execute command and parse JSON output\n    func executeJSON<T: Decodable>(\n        args: [String],\n        directory: String? = nil\n    ) async throws -> T {\n        let (status, output) = try await execute(args: args, directory: directory)\n\n        guard status == 0 else {\n            let errorOutput = String(data: output, encoding: .utf8) ?? \"\"\n            throw Error.commandFailed(status, errorOutput)\n        }\n\n        do {\n            return try JSONDecoder().decode(T.self, from: output)\n        } catch {\n            let outputStr = String(data: output, encoding: .utf8) ?? \"\"\n            throw Error.invalidJSON(\"failed to decode: \\(error), output: \\(outputStr)\")\n        }\n    }\n\n    /// Execute command without capturing output\n    func executeVoid(\n        args: [String],\n        stdin: FileHandle? = nil,\n        stdout: FileHandle? = nil,\n        stderr: FileHandle? = nil,\n        extraFiles: [FileHandle] = [],\n        directory: String? = nil\n    ) async throws {\n        let (status, output) = try await execute(\n            args: args,\n            stdin: stdin,\n            stdout: stdout,\n            stderr: stderr,\n            extraFiles: extraFiles,\n            directory: directory\n        )\n\n        guard status == 0 else {\n            let errorOutput = String(data: output, encoding: .utf8) ?? \"\"\n            throw Error.commandFailed(status, errorOutput)\n        }\n    }\n\n    /// Read PID from a file\n    func readPidFile(_ path: String) throws -> Int {\n        guard let data = try? Data(contentsOf: URL(fileURLWithPath: path)),\n            let pidString = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines),\n            let pid = Int(pidString)\n        else {\n            throw Error.invalidPidFile(path)\n        }\n        return pid\n    }\n}\n\nextension Runc {\n    /// Create a container\n    func create(\n        id: String,\n        bundle: String,\n        opts: CreateOpts = CreateOpts()\n    ) async throws -> Int? {\n        var args = baseArgs() + [\"create\"]\n\n        if let pidFile = opts.pidFile {\n            args += [\"--pid-file\", pidFile]\n        }\n\n        if let consoleSocket = opts.consoleSocket {\n            args += [\"--console-socket\", consoleSocket]\n        }\n\n        if opts.detach {\n            args.append(\"--detach\")\n        }\n\n        if opts.noPivot {\n            args.append(\"--no-pivot\")\n        }\n\n        if opts.noNewKeyring {\n            args.append(\"--no-new-keyring\")\n        }\n\n        args += [\"--bundle\", bundle, id]\n\n        try await executeVoid(\n            args: args,\n            stdin: opts.io.stdin,\n            stdout: opts.io.stdout,\n            stderr: opts.io.stderr,\n            extraFiles: opts.extraFiles,\n            directory: bundle\n        )\n\n        // Read PID if pidFile was specified\n        if let pidFile = opts.pidFile {\n            return try readPidFile(pidFile)\n        }\n\n        return nil\n    }\n\n    /// Start a container\n    func start(id: String) async throws {\n        let args = baseArgs() + [\"start\", id]\n        try await executeVoid(args: args)\n    }\n\n    /// Run a container (create + start)\n    func run(\n        id: String,\n        bundle: String,\n        opts: CreateOpts = CreateOpts()\n    ) async throws -> Int? {\n        var args = baseArgs() + [\"run\"]\n\n        if let pidFile = opts.pidFile {\n            args += [\"--pid-file\", pidFile]\n        }\n\n        if let consoleSocket = opts.consoleSocket {\n            args += [\"--console-socket\", consoleSocket]\n        }\n\n        if opts.detach {\n            args.append(\"--detach\")\n        }\n\n        if opts.noPivot {\n            args.append(\"--no-pivot\")\n        }\n\n        if opts.noNewKeyring {\n            args.append(\"--no-new-keyring\")\n        }\n\n        args += [\"--bundle\", bundle, id]\n\n        try await executeVoid(\n            args: args,\n            stdin: opts.io.stdin,\n            stdout: opts.io.stdout,\n            stderr: opts.io.stderr,\n            extraFiles: opts.extraFiles,\n            directory: bundle\n        )\n\n        // Read PID if pidFile was specified\n        if let pidFile = opts.pidFile {\n            return try readPidFile(pidFile)\n        }\n\n        return nil\n    }\n\n    /// Delete a container\n    func delete(id: String, opts: DeleteOpts = DeleteOpts()) async throws {\n        var args = baseArgs() + [\"delete\"]\n\n        if opts.force {\n            args.append(\"--force\")\n        }\n\n        args.append(id)\n\n        try await executeVoid(args: args)\n    }\n\n    /// Send a signal to a container\n    func kill(id: String, signal: Int32, all: Bool = false) async throws {\n        var args = baseArgs() + [\"kill\"]\n\n        if all {\n            args.append(\"--all\")\n        }\n\n        args += [id, String(signal)]\n\n        try await executeVoid(args: args)\n    }\n\n    /// Pause a container\n    func pause(id: String) async throws {\n        let args = baseArgs() + [\"pause\", id]\n        try await executeVoid(args: args)\n    }\n\n    /// Resume a paused container\n    func resume(id: String) async throws {\n        let args = baseArgs() + [\"resume\", id]\n        try await executeVoid(args: args)\n    }\n\n    /// Execute a process in a running container\n    func exec(\n        id: String,\n        processSpec: String,\n        opts: ExecOpts = ExecOpts()\n    ) async throws -> Int? {\n        var args = baseArgs() + [\"exec\"]\n\n        if let pidFile = opts.pidFile {\n            args += [\"--pid-file\", pidFile]\n        }\n\n        if let consoleSocket = opts.consoleSocket {\n            args += [\"--console-socket\", consoleSocket]\n        }\n\n        if opts.detach {\n            args.append(\"--detach\")\n        }\n\n        if let processPath = opts.processPath {\n            args += [\"--process\", processPath]\n        }\n\n        args += [id, processSpec]\n\n        try await executeVoid(\n            args: args,\n            stdin: opts.io.stdin,\n            stdout: opts.io.stdout,\n            stderr: opts.io.stderr\n        )\n\n        // Read PID if pidFile was specified\n        if let pidFile = opts.pidFile {\n            return try readPidFile(pidFile)\n        }\n\n        return nil\n    }\n\n    /// Update container resources\n    func update(id: String, resources: String) async throws {\n        let args = baseArgs() + [\"update\", \"--resources\", resources, id]\n        try await executeVoid(args: args)\n    }\n\n    /// Checkpoint a container\n    func checkpoint(\n        id: String,\n        imagePath: String,\n        leaveRunning: Bool = false,\n        workPath: String? = nil\n    ) async throws {\n        var args = baseArgs() + [\"checkpoint\"]\n\n        if leaveRunning {\n            args.append(\"--leave-running\")\n        }\n\n        if let workPath = workPath {\n            args += [\"--work-path\", workPath]\n        }\n\n        args += [\"--image-path\", imagePath, id]\n\n        try await executeVoid(args: args)\n    }\n\n    /// Restore a container from checkpoint\n    func restore(\n        id: String,\n        bundle: String,\n        opts: RestoreOpts = RestoreOpts()\n    ) async throws -> Int? {\n        var args = baseArgs() + [\"restore\"]\n\n        if let pidFile = opts.pidFile {\n            args += [\"--pid-file\", pidFile]\n        }\n\n        if let consoleSocket = opts.consoleSocket {\n            args += [\"--console-socket\", consoleSocket]\n        }\n\n        if opts.detach {\n            args.append(\"--detach\")\n        }\n\n        if opts.noPivot {\n            args.append(\"--no-pivot\")\n        }\n\n        if opts.noNewKeyring {\n            args.append(\"--no-new-keyring\")\n        }\n\n        if let imagePath = opts.imagePath {\n            args += [\"--image-path\", imagePath]\n        }\n\n        if let parentPath = opts.parentPath {\n            args += [\"--parent-path\", parentPath]\n        }\n\n        if let workPath = opts.workPath {\n            args += [\"--work-path\", workPath]\n        }\n\n        args += [\"--bundle\", bundle, id]\n\n        try await executeVoid(args: args, directory: bundle)\n\n        if let pidFile = opts.pidFile {\n            return try readPidFile(pidFile)\n        }\n\n        return nil\n    }\n}\n\n// MARK: - List and State Operations\n\nextension Runc {\n    /// List all containers\n    func list() async throws -> [Container] {\n        let args = baseArgs() + [\"list\", \"--format\", \"json\"]\n        let containers: [Container] = try await executeJSON(args: args)\n        return containers\n    }\n\n    /// Get state of a specific container\n    func state(id: String) async throws -> ContainerizationOCI.State {\n        let args = baseArgs() + [\"state\", id]\n        let state: ContainerizationOCI.State = try await executeJSON(args: args)\n        return state\n    }\n\n    /// List process IDs in a container\n    func ps(id: String) async throws -> [Int] {\n        let args = baseArgs() + [\"ps\", \"--format\", \"json\", id]\n        let (status, output) = try await execute(args: args)\n\n        guard status == 0 else {\n            let errorOutput = String(data: output, encoding: .utf8) ?? \"\"\n            throw Error.commandFailed(status, errorOutput)\n        }\n\n        // ps output is just an array of PIDs\n        let pids = try JSONDecoder().decode([Int].self, from: output)\n        return pids\n    }\n\n    /// Get version information\n    func version() async throws -> String {\n        let args = [command, \"--version\"]\n        let (status, output) = try await execute(args: args)\n\n        guard status == 0 else {\n            let errorOutput = String(data: output, encoding: .utf8) ?? \"\"\n            throw Error.commandFailed(status, errorOutput)\n        }\n\n        return String(data: output, encoding: .utf8) ?? \"\"\n    }\n}\n\n// MARK: - Events\n\nextension Runc {\n    /// Event from container runtime\n    struct Event: Codable, Sendable {\n        let type: String\n        let id: String\n        let stats: EventStats?\n\n        enum CodingKeys: String, CodingKey {\n            case type\n            case id\n            case stats\n        }\n    }\n\n    /// Statistics in an event\n    struct EventStats: Codable, Sendable {\n        let cpu: CPUStats?\n        let memory: MemoryStats?\n        let pids: PIDStats?\n\n        enum CodingKeys: String, CodingKey {\n            case cpu\n            case memory\n            case pids\n        }\n    }\n\n    struct CPUStats: Codable, Sendable {\n        let usage: CPUUsage?\n        let throttling: ThrottlingData?\n\n        struct CPUUsage: Codable, Sendable {\n            let total: UInt64?\n            let percpu: [UInt64]?\n        }\n\n        struct ThrottlingData: Codable, Sendable {\n            let periods: UInt64?\n            let throttledPeriods: UInt64?\n            let throttledTime: UInt64?\n        }\n    }\n\n    struct MemoryStats: Codable, Sendable {\n        let usage: MemoryUsage?\n        let limit: UInt64?\n\n        struct MemoryUsage: Codable, Sendable {\n            let usage: UInt64?\n            let max: UInt64?\n        }\n    }\n\n    struct PIDStats: Codable, Sendable {\n        let current: UInt64?\n        let limit: UInt64?\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/RuncProcess.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\n#if os(Linux)\n\nimport Containerization\nimport ContainerizationError\nimport ContainerizationOCI\nimport ContainerizationOS\nimport Foundation\nimport Logging\nimport Synchronization\n\n/// A container process implementation that uses runc as the OCI runtime\nfinal class RuncProcess: ContainerProcess, Sendable {\n    // swiftlint: disable type_name\n    protocol IO: Sendable {\n        func attachConsole(fd: Int32) throws\n        func create() throws\n        func getIO() -> Runc.IO\n        func closeAfterExec() throws\n        func resize(size: Terminal.Size) throws\n        func close() throws\n        func closeStdin() throws\n    }\n    // swiftlint: enable type_name\n\n    private enum ProcessState {\n        case initial\n        case creating\n        case running(pid: Int32)\n        case exited(ContainerExitStatus)\n    }\n\n    private struct State {\n        var state: ProcessState = .initial\n        var waiters: [CheckedContinuation<ContainerExitStatus, Never>] = []\n    }\n\n    let id: String\n\n    private let log: Logger\n    private let runc: Runc\n    private let io: IO\n    private let state: Mutex<State>\n    private let terminal: Bool\n    private let bundle: ContainerizationOCI.Bundle\n    private let consoleSocket: ConsoleSocket?\n\n    var pid: Int32? {\n        self.state.withLock {\n            switch $0.state {\n            case .running(let pid):\n                return pid\n            default:\n                return nil\n            }\n        }\n    }\n\n    init(\n        id: String,\n        stdio: HostStdio,\n        bundle: ContainerizationOCI.Bundle,\n        runc: Runc,\n        log: Logger\n    ) throws {\n        self.id = id\n        var log = log\n        log[metadataKey: \"id\"] = \"\\(id)\"\n        self.log = log\n        self.runc = runc\n        self.bundle = bundle\n        self.terminal = stdio.terminal\n\n        var io: IO\n        var consoleSocket: ConsoleSocket? = nil\n\n        if stdio.terminal {\n            log.info(\"setting up terminal I/O for runc\")\n            let socket = try ConsoleSocket.temporary()\n            consoleSocket = socket\n            io = try RuncTerminalIO(\n                stdio: stdio,\n                log: log\n            )\n        } else {\n            io = RuncStandardIO(\n                stdio: stdio,\n                log: log\n            )\n        }\n\n        log.info(\"starting I/O for runc\")\n        try io.create()\n\n        self.consoleSocket = consoleSocket\n        self.io = io\n        self.state = Mutex(State())\n    }\n\n    func start() async throws -> Int32 {\n        try self.state.withLock {\n            guard case .initial = $0.state else {\n                throw ContainerizationError(\n                    .invalidState,\n                    message: \"container already started\"\n                )\n            }\n            $0.state = .creating\n        }\n\n        log.info(\n            \"starting runc process\",\n            metadata: [\n                \"id\": \"\\(id)\"\n            ])\n\n        let pidFilePath = self.bundle.path.appendingPathComponent(\"runc-pid\").path\n        let runcIO = self.io.getIO()\n\n        let opts: CreateOpts\n        if let consoleSocket {\n            opts = CreateOpts(\n                pidFile: pidFilePath,\n                consoleSocket: consoleSocket.path,\n                io: runcIO\n            )\n        } else {\n            opts = CreateOpts(\n                pidFile: pidFilePath,\n                io: runcIO\n            )\n        }\n\n        guard\n            let pidInt = try await self.runc.create(\n                id: self.id,\n                bundle: self.bundle.path.path,\n                opts: opts\n            )\n        else {\n            throw ContainerizationError(\n                .internalError,\n                message: \"runc create did not return a PID\"\n            )\n        }\n\n        let pid = Int32(pidInt)\n\n        self.log.info(\n            \"container created\",\n            metadata: [\n                \"pid\": \"\\(pid)\"\n            ])\n\n        // Close the pipe ends we gave to runc now that it has inherited them\n        // and attach console if in terminal mode\n        if self.terminal, let consoleSocket = self.consoleSocket {\n            self.log.info(\"waiting for console FD from runc\")\n            let ptyFd = try consoleSocket.receiveMaster()\n\n            self.log.info(\n                \"received PTY FD from runc, attaching\",\n                metadata: [\n                    \"id\": \"\\(self.id)\"\n                ])\n\n            try self.io.closeAfterExec()\n            try self.io.attachConsole(fd: ptyFd)\n        } else {\n            try self.io.closeAfterExec()\n        }\n\n        try await self.runc.start(id: self.id)\n\n        self.state.withLock {\n            $0.state = .running(pid: pid)\n        }\n\n        self.log.info(\n            \"started runc process\",\n            metadata: [\n                \"pid\": \"\\(pid)\",\n                \"id\": \"\\(self.id)\",\n            ])\n\n        return pid\n    }\n\n    func setExit(_ status: Int32) {\n        self.state.withLock {\n            self.log.info(\n                \"runc process exit\",\n                metadata: [\n                    \"status\": \"\\(status)\"\n                ])\n\n            let exitStatus = ContainerExitStatus(exitCode: status, exitedAt: Date.now)\n            $0.state = .exited(exitStatus)\n\n            do {\n                try self.io.close()\n            } catch {\n                self.log.error(\"failed to close I/O for process: \\(error)\")\n            }\n\n            for waiter in $0.waiters {\n                waiter.resume(returning: exitStatus)\n            }\n\n            self.log.debug(\"\\($0.waiters.count) runc process waiters signaled\")\n            $0.waiters.removeAll()\n        }\n    }\n\n    func wait() async -> ContainerExitStatus {\n        await withCheckedContinuation { cont in\n            self.state.withLock {\n                if case .exited(let exitStatus) = $0.state {\n                    cont.resume(returning: exitStatus)\n                    return\n                }\n                $0.waiters.append(cont)\n            }\n        }\n    }\n\n    func kill(_ signal: Int32) async throws {\n        self.log.info(\"sending signal \\(signal) to runc container \\(id)\")\n        try await self.runc.kill(id: self.id, signal: signal)\n    }\n\n    func resize(size: Terminal.Size) throws {\n        try self.state.withLock {\n            if case .exited = $0.state {\n                return\n            }\n            try self.io.resize(size: size)\n        }\n    }\n\n    func closeStdin() throws {\n        try self.io.closeStdin()\n    }\n\n    func delete() async throws {\n        let shouldDelete = self.state.withLock { state -> Bool in\n            switch state.state {\n            case .initial, .creating:\n                return false\n            default:\n                return true\n            }\n        }\n\n        guard shouldDelete else {\n            log.info(\"container was never created, skipping delete\")\n            return\n        }\n\n        log.info(\"deleting runc container\", metadata: [\"id\": \"\\(id)\"])\n\n        try await self.runc.delete(\n            id: self.id,\n            opts: DeleteOpts(force: true)\n        )\n\n        if let consoleSocket = self.consoleSocket {\n            try consoleSocket.close()\n        }\n    }\n}\n\n// MARK: - RuncTerminalIO\n\nfinal class RuncTerminalIO: RuncProcess.IO & Sendable {\n    private struct State {\n        var stdinSocket: Socket?\n        var stdoutSocket: Socket?\n\n        var stdin: IOPair?\n        var stdout: IOPair?\n        var terminal: Terminal?\n    }\n\n    private let log: Logger?\n    private let hostStdio: HostStdio\n    private let state: Mutex<State>\n\n    init(\n        stdio: HostStdio,\n        log: Logger?\n    ) throws {\n        self.hostStdio = stdio\n        self.log = log\n        self.state = Mutex(State())\n    }\n\n    func resize(size: Terminal.Size) throws {\n        try self.state.withLock {\n            if let terminal = $0.terminal {\n                try terminal.resize(size: size)\n            }\n        }\n    }\n\n    func create() throws {\n        try self.state.withLock {\n            if let stdinPort = self.hostStdio.stdin {\n                let type = VsockType(\n                    port: stdinPort,\n                    cid: VsockType.hostCID\n                )\n                let stdinSocket = try Socket(type: type, closeOnDeinit: false)\n                try stdinSocket.connect()\n                $0.stdinSocket = stdinSocket\n            }\n\n            if let stdoutPort = self.hostStdio.stdout {\n                let type = VsockType(\n                    port: stdoutPort,\n                    cid: VsockType.hostCID\n                )\n                let stdoutSocket = try Socket(type: type, closeOnDeinit: false)\n                try stdoutSocket.connect()\n                $0.stdoutSocket = stdoutSocket\n            }\n        }\n    }\n\n    func getIO() -> Runc.IO {\n        // Terminal mode doesn't pass pipes to runc, it uses the console socket\n        .inherit\n    }\n\n    func closeAfterExec() throws {\n        // No pipes to close in terminal mode\n    }\n\n    func attachConsole(fd: Int32) throws {\n        try self.state.withLock {\n            let term = try Terminal(descriptor: fd, setInitState: false)\n            $0.terminal = term\n\n            if let stdinSocket = $0.stdinSocket {\n                let pair = IOPair(\n                    readFrom: stdinSocket,\n                    writeTo: term,\n                    reason: \"RuncTerminalIO stdin\",\n                    logger: log\n                )\n                try pair.relay(ignoreHup: true)\n                $0.stdin = pair\n            }\n\n            if let stdoutSocket = $0.stdoutSocket {\n                let pair = IOPair(\n                    readFrom: term,\n                    writeTo: stdoutSocket,\n                    reason: \"RuncTerminalIO stdout\",\n                    logger: log\n                )\n                try pair.relay(ignoreHup: true)\n                $0.stdout = pair\n            }\n        }\n    }\n\n    func close() throws {\n        self.state.withLock {\n            if let stdin = $0.stdin {\n                stdin.close()\n                $0.stdin = nil\n            }\n            if let stdout = $0.stdout {\n                stdout.close()\n                $0.stdout = nil\n            }\n            $0.terminal = nil\n        }\n    }\n\n    func closeStdin() throws {\n        self.state.withLock {\n            if let stdin = $0.stdin {\n                stdin.close()\n                $0.stdin = nil\n            }\n        }\n    }\n}\n\n// MARK: - RuncStandardIO\n\nfinal class RuncStandardIO: RuncProcess.IO & Sendable {\n    private struct State {\n        var stdin: IOPair?\n        var stdout: IOPair?\n        var stderr: IOPair?\n\n        var stdinPipe: Pipe?\n        var stdoutPipe: Pipe?\n        var stderrPipe: Pipe?\n    }\n\n    private let log: Logger?\n    private let hostStdio: HostStdio\n    private let state: Mutex<State>\n\n    init(\n        stdio: HostStdio,\n        log: Logger?\n    ) {\n        self.hostStdio = stdio\n        self.log = log\n        self.state = Mutex(State())\n    }\n\n    // NOP for non-terminal\n    func attachConsole(fd: Int32) throws {}\n\n    func create() throws {\n        try self.state.withLock {\n            if let stdinPort = self.hostStdio.stdin {\n                let inPipe = Pipe()\n                $0.stdinPipe = inPipe\n\n                let type = VsockType(\n                    port: stdinPort,\n                    cid: VsockType.hostCID\n                )\n                let stdinSocket = try Socket(type: type, closeOnDeinit: false)\n                try stdinSocket.connect()\n\n                let pair = IOPair(\n                    readFrom: stdinSocket,\n                    writeTo: inPipe.fileHandleForWriting,\n                    reason: \"RuncStandardIO stdin\",\n                    logger: log\n                )\n                $0.stdin = pair\n                try pair.relay()\n            }\n\n            if let stdoutPort = self.hostStdio.stdout {\n                let outPipe = Pipe()\n                $0.stdoutPipe = outPipe\n\n                let type = VsockType(\n                    port: stdoutPort,\n                    cid: VsockType.hostCID\n                )\n                let stdoutSocket = try Socket(type: type, closeOnDeinit: false)\n                try stdoutSocket.connect()\n\n                let pair = IOPair(\n                    readFrom: outPipe.fileHandleForReading,\n                    writeTo: stdoutSocket,\n                    reason: \"RuncStandardIO stdout\",\n                    logger: log\n                )\n                $0.stdout = pair\n                try pair.relay()\n            }\n\n            if let stderrPort = self.hostStdio.stderr {\n                let errPipe = Pipe()\n                $0.stderrPipe = errPipe\n\n                let type = VsockType(\n                    port: stderrPort,\n                    cid: VsockType.hostCID\n                )\n                let stderrSocket = try Socket(type: type, closeOnDeinit: false)\n                try stderrSocket.connect()\n\n                let pair = IOPair(\n                    readFrom: errPipe.fileHandleForReading,\n                    writeTo: stderrSocket,\n                    reason: \"RuncStandardIO stderr\",\n                    logger: log\n                )\n                $0.stderr = pair\n                try pair.relay()\n            }\n        }\n    }\n\n    func getIO() -> Runc.IO {\n        self.state.withLock {\n            Runc.IO(\n                stdin: $0.stdinPipe?.fileHandleForReading,\n                stdout: $0.stdoutPipe?.fileHandleForWriting,\n                stderr: $0.stderrPipe?.fileHandleForWriting\n            )\n        }\n    }\n\n    func closeAfterExec() throws {\n        try self.state.withLock {\n            // Close the pipe ends we gave to runc (the child inherited them)\n            if let stdinPipe = $0.stdinPipe {\n                try stdinPipe.fileHandleForReading.close()\n                $0.stdinPipe = nil\n            }\n            if let stdoutPipe = $0.stdoutPipe {\n                try stdoutPipe.fileHandleForWriting.close()\n                $0.stdoutPipe = nil\n            }\n            if let stderrPipe = $0.stderrPipe {\n                try stderrPipe.fileHandleForWriting.close()\n                $0.stderrPipe = nil\n            }\n        }\n    }\n\n    func resize(size: Terminal.Size) throws {\n        throw ContainerizationError(.unsupported, message: \"resize not supported for standard IO\")\n    }\n\n    func close() throws {\n        self.state.withLock {\n            if let stdin = $0.stdin {\n                stdin.close()\n                $0.stdin = nil\n            }\n\n            if let stdout = $0.stdout {\n                stdout.close()\n                $0.stdout = nil\n            }\n\n            if let stderr = $0.stderr {\n                stderr.close()\n                $0.stderr = nil\n            }\n        }\n    }\n\n    func closeStdin() throws {\n        self.state.withLock {\n            if let stdin = $0.stdin {\n                stdin.close()\n                $0.stdin = nil\n            }\n        }\n    }\n}\n\n#endif  // os(Linux)\n"
  },
  {
    "path": "vminitd/Sources/vminitd/Server+GRPC.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport Cgroup\nimport Containerization\nimport ContainerizationArchive\nimport ContainerizationError\nimport ContainerizationExtras\nimport ContainerizationNetlink\nimport ContainerizationOCI\nimport ContainerizationOS\nimport Foundation\nimport GRPC\nimport Logging\nimport NIOCore\nimport NIOPosix\nimport SwiftProtobuf\n\nprivate let _setenv = Foundation.setenv\n\n#if canImport(Musl)\nimport Musl\nprivate let _mount = Musl.mount\nprivate let _umount = Musl.umount2\nprivate let _kill = Musl.kill\nprivate let _sync = Musl.sync\n#elseif canImport(Glibc)\nimport Glibc\nprivate let _mount = Glibc.mount\nprivate let _umount = Glibc.umount2\nprivate let _kill = Glibc.kill\nprivate let _sync = Glibc.sync\n#endif\n\nextension ContainerizationError {\n    func toGRPCStatus(operation: String) -> GRPCStatus {\n        let message = \"\\(operation): \\(self)\"\n        let code: GRPCStatus.Code = {\n            switch self.code {\n            case .invalidArgument:\n                return .invalidArgument\n            case .notFound:\n                return .notFound\n            case .exists:\n                return .alreadyExists\n            case .cancelled:\n                return .cancelled\n            case .unsupported:\n                return .unimplemented\n            case .unknown:\n                return .unknown\n            case .internalError:\n                return .internalError\n            case .interrupted:\n                return .unavailable\n            case .invalidState:\n                return .failedPrecondition\n            case .timeout:\n                return .deadlineExceeded\n            default:\n                return .internalError\n            }\n        }()\n        return GRPCStatus(code: code, message: message, cause: self)\n    }\n}\n\nextension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvider {\n    func setTime(\n        request: Com_Apple_Containerization_Sandbox_V3_SetTimeRequest,\n        context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_SetTimeResponse {\n        log.trace(\n            \"setTime\",\n            metadata: [\n                \"sec\": \"\\(request.sec)\",\n                \"usec\": \"\\(request.usec)\",\n            ])\n\n        var tv = timeval(tv_sec: time_t(request.sec), tv_usec: suseconds_t(request.usec))\n        guard settimeofday(&tv, nil) == 0 else {\n            let error = swiftErrno(\"settimeofday\")\n            log.error(\n                \"setTime\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n            throw GRPCStatus(code: .internalError, message: \"failed to settimeofday: \\(error)\")\n        }\n\n        return .init()\n    }\n\n    func setupEmulator(\n        request: Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest,\n        context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse {\n        log.debug(\n            \"setupEmulator\",\n            metadata: [\n                \"request\": \"\\(request)\"\n            ])\n\n        if !Binfmt.mounted() {\n            throw GRPCStatus(\n                code: .internalError,\n                message: \"\\(Binfmt.path) is not mounted\"\n            )\n        }\n\n        do {\n            let bfmt = Binfmt.Entry(\n                name: request.name,\n                type: request.type,\n                offset: request.offset,\n                magic: request.magic,\n                mask: request.mask,\n                flags: request.flags\n            )\n            try bfmt.register(binaryPath: request.binaryPath)\n        } catch {\n            log.error(\n                \"setupEmulator\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n            throw GRPCStatus(\n                code: .internalError,\n                message: \"setupEmulator: failed to register binfmt_misc entry: \\(error)\"\n            )\n        }\n\n        return .init()\n    }\n\n    func sysctl(\n        request: Com_Apple_Containerization_Sandbox_V3_SysctlRequest,\n        context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_SysctlResponse {\n        log.debug(\n            \"sysctl\",\n            metadata: [\n                \"settings\": \"\\(request.settings)\"\n            ])\n\n        do {\n            let sysctlPath = URL(fileURLWithPath: \"/proc/sys/\")\n            for (k, v) in request.settings {\n                guard let data = v.data(using: .ascii) else {\n                    throw GRPCStatus(code: .internalError, message: \"failed to convert \\(v) to data buffer for sysctl write\")\n                }\n\n                let setting =\n                    sysctlPath\n                    .appendingPathComponent(k.replacingOccurrences(of: \".\", with: \"/\"))\n                let fh = try FileHandle(forWritingTo: setting)\n                defer { try? fh.close() }\n\n                try fh.write(contentsOf: data)\n            }\n        } catch {\n            log.error(\n                \"sysctl\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n            throw GRPCStatus(\n                code: .internalError,\n                message: \"sysctl: failed to set sysctl: \\(error)\"\n            )\n        }\n\n        return .init()\n    }\n\n    func proxyVsock(\n        request: Com_Apple_Containerization_Sandbox_V3_ProxyVsockRequest,\n        context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_ProxyVsockResponse {\n        log.debug(\n            \"proxyVsock\",\n            metadata: [\n                \"id\": \"\\(request.id)\",\n                \"port\": \"\\(request.vsockPort)\",\n                \"guestPath\": \"\\(request.guestPath)\",\n                \"action\": \"\\(request.action)\",\n            ])\n\n        let proxy = VsockProxy(\n            id: request.id,\n            action: request.action == .into ? .dial : .listen,\n            port: request.vsockPort,\n            path: URL(fileURLWithPath: request.guestPath),\n            udsPerms: request.guestSocketPermissions,\n            log: log\n        )\n\n        do {\n            try await proxy.start()\n            try await state.add(proxy: proxy)\n        } catch {\n            try? await proxy.close()\n            log.error(\n                \"proxyVsock\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n            throw GRPCStatus(\n                code: .internalError,\n                message: \"proxyVsock: failed to setup vsock proxy: \\(error)\"\n            )\n        }\n\n        log.info(\n            \"proxyVsock started\",\n            metadata: [\n                \"id\": \"\\(request.id)\",\n                \"port\": \"\\(request.vsockPort)\",\n                \"guestPath\": \"\\(request.guestPath)\",\n            ])\n\n        return .init()\n    }\n\n    func stopVsockProxy(\n        request: Com_Apple_Containerization_Sandbox_V3_StopVsockProxyRequest,\n        context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_StopVsockProxyResponse {\n        log.debug(\n            \"stopVsockProxy\",\n            metadata: [\n                \"id\": \"\\(request.id)\"\n            ])\n\n        do {\n            let proxy = try await state.remove(proxy: request.id)\n            try await proxy.close()\n        } catch {\n            log.error(\n                \"stopVsockProxy\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n            throw GRPCStatus(\n                code: .internalError,\n                message: \"stopVsockProxy: failed to stop vsock proxy: \\(error)\"\n            )\n        }\n\n        log.info(\n            \"stopVsockProxy completed\",\n            metadata: [\n                \"id\": \"\\(request.id)\"\n            ])\n\n        return .init()\n    }\n\n    func mkdir(request: Com_Apple_Containerization_Sandbox_V3_MkdirRequest, context: GRPC.GRPCAsyncServerCallContext)\n        async throws -> Com_Apple_Containerization_Sandbox_V3_MkdirResponse\n    {\n        log.debug(\n            \"mkdir\",\n            metadata: [\n                \"path\": \"\\(request.path)\",\n                \"all\": \"\\(request.all)\",\n            ])\n\n        do {\n            try FileManager.default.createDirectory(\n                atPath: request.path,\n                withIntermediateDirectories: request.all\n            )\n        } catch {\n            log.error(\n                \"mkdir\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n            throw GRPCStatus(code: .internalError, message: \"mkdir: \\(error)\")\n        }\n\n        return .init()\n    }\n\n    func writeFile(request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, context: GRPC.GRPCAsyncServerCallContext)\n        async throws -> Com_Apple_Containerization_Sandbox_V3_WriteFileResponse\n    {\n        log.debug(\n            \"writeFile\",\n            metadata: [\n                \"path\": \"\\(request.path)\",\n                \"mode\": \"\\(request.mode)\",\n                \"dataSize\": \"\\(request.data.count)\",\n            ])\n\n        do {\n            if request.flags.createParentDirs {\n                let fileURL = URL(fileURLWithPath: request.path)\n                let parentDir = fileURL.deletingLastPathComponent()\n                try FileManager.default.createDirectory(\n                    at: parentDir,\n                    withIntermediateDirectories: true\n                )\n            }\n\n            var flags = O_WRONLY\n            if request.flags.createIfMissing {\n                flags |= O_CREAT\n            }\n            if request.flags.append {\n                flags |= O_APPEND\n            }\n\n            let mode = request.mode > 0 ? mode_t(request.mode) : mode_t(0644)\n            let fd = open(request.path, flags, mode)\n            guard fd != -1 else {\n                let error = swiftErrno(\"open\")\n                throw GRPCStatus(\n                    code: .internalError,\n                    message: \"writeFile: failed to open file: \\(error)\"\n                )\n            }\n\n            let fh = FileHandle(fileDescriptor: fd, closeOnDealloc: true)\n            try fh.write(contentsOf: request.data)\n        } catch {\n            log.error(\n                \"writeFile\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n            if error is GRPCStatus {\n                throw error\n            }\n            throw GRPCStatus(\n                code: .internalError,\n                message: \"writeFile: \\(error)\"\n            )\n        }\n\n        return .init()\n    }\n\n    // Chunk size for streaming file transfers (1MB).\n    private static let copyChunkSize = 1024 * 1024\n\n    func copy(\n        request: Com_Apple_Containerization_Sandbox_V3_CopyRequest,\n        responseStream: GRPCAsyncResponseStreamWriter<Com_Apple_Containerization_Sandbox_V3_CopyResponse>,\n        context: GRPC.GRPCAsyncServerCallContext\n    ) async throws {\n        let path = request.path\n        let vsockPort = request.vsockPort\n\n        log.debug(\n            \"copy\",\n            metadata: [\n                \"direction\": \"\\(request.direction)\",\n                \"path\": \"\\(path)\",\n                \"vsockPort\": \"\\(vsockPort)\",\n                \"isArchive\": \"\\(request.isArchive)\",\n                \"mode\": \"\\(request.mode)\",\n                \"createParents\": \"\\(request.createParents)\",\n            ])\n\n        do {\n            switch request.direction {\n            case .copyIn:\n                try await handleCopyIn(request: request, responseStream: responseStream)\n            case .copyOut:\n                try await handleCopyOut(request: request, responseStream: responseStream)\n            case .UNRECOGNIZED(let value):\n                throw GRPCStatus(code: .invalidArgument, message: \"copy: unrecognized direction \\(value)\")\n            }\n        } catch {\n            log.error(\n                \"copy\",\n                metadata: [\n                    \"direction\": \"\\(request.direction)\",\n                    \"path\": \"\\(path)\",\n                    \"error\": \"\\(error)\",\n                ])\n            if error is GRPCStatus {\n                throw error\n            }\n            throw GRPCStatus(code: .internalError, message: \"copy: \\(error)\")\n        }\n    }\n\n    /// Handle a COPY_IN request: connect to host vsock port, read data, write to guest filesystem.\n    private func handleCopyIn(\n        request: Com_Apple_Containerization_Sandbox_V3_CopyRequest,\n        responseStream: GRPCAsyncResponseStreamWriter<Com_Apple_Containerization_Sandbox_V3_CopyResponse>\n    ) async throws {\n        let path = request.path\n        let isArchive = request.isArchive\n\n        if request.createParents {\n            let parentDir = URL(fileURLWithPath: path).deletingLastPathComponent()\n            try FileManager.default.createDirectory(at: parentDir, withIntermediateDirectories: true)\n        }\n\n        // Connect to the host's vsock port for data transfer.\n        let vsockType = VsockType(port: request.vsockPort, cid: VsockType.hostCID)\n        let sock = try Socket(type: vsockType, closeOnDeinit: false)\n        try sock.connect()\n        let sockFd = sock.fileDescriptor\n\n        // Dispatch blocking I/O onto the thread pool.\n        let rejected: [String] = try await blockingPool.runIfActive { [self] in\n            defer { try? sock.close() }\n\n            guard isArchive else {\n                let mode = request.mode > 0 ? mode_t(request.mode) : mode_t(0o644)\n                let fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, mode)\n                guard fd != -1 else {\n                    throw GRPCStatus(\n                        code: .internalError,\n                        message: \"copy: failed to open file '\\(path)': \\(swiftErrno(\"open\"))\"\n                    )\n                }\n                defer { close(fd) }\n\n                var buf = [UInt8](repeating: 0, count: Self.copyChunkSize)\n                while true {\n                    let n = read(sockFd, &buf, buf.count)\n                    if n == 0 { break }\n                    guard n > 0 else {\n                        throw GRPCStatus(\n                            code: .internalError,\n                            message: \"copy: vsock read error: \\(swiftErrno(\"read\"))\"\n                        )\n                    }\n                    var written = 0\n                    while written < n {\n                        let w = buf.withUnsafeBytes { ptr in\n                            write(fd, ptr.baseAddress! + written, n - written)\n                        }\n                        guard w > 0 else {\n                            throw GRPCStatus(\n                                code: .internalError,\n                                message: \"copy: write error: \\(swiftErrno(\"write\"))\"\n                            )\n                        }\n                        written += w\n                    }\n                }\n                return []\n            }\n            let destURL = URL(fileURLWithPath: path)\n            try FileManager.default.createDirectory(at: destURL, withIntermediateDirectories: true)\n\n            let fileHandle = FileHandle(fileDescriptor: sockFd, closeOnDealloc: false)\n            let reader = try ArchiveReader(format: .pax, filter: .gzip, fileHandle: fileHandle)\n            return try reader.extractContents(to: destURL)\n        }\n\n        if !rejected.isEmpty {\n            log.info(\"copy: archive extracted\", metadata: [\"path\": \"\\(path)\", \"rejectedCount\": \"\\(rejected.count)\"])\n            for rejectedPath in rejected {\n                log.error(\"copy: rejected archive path\", metadata: [\"path\": \"\\(rejectedPath)\"])\n            }\n        }\n\n        log.debug(\"copy: copyIn complete\", metadata: [\"path\": \"\\(path)\", \"isArchive\": \"\\(isArchive)\"])\n\n        // Send completion response.\n        try await responseStream.send(.with { $0.status = .complete })\n    }\n\n    /// Handle a COPY_OUT request: stat path, send metadata, connect to host vsock port, write data.\n    private func handleCopyOut(\n        request: Com_Apple_Containerization_Sandbox_V3_CopyRequest,\n        responseStream: GRPCAsyncResponseStreamWriter<Com_Apple_Containerization_Sandbox_V3_CopyResponse>\n    ) async throws {\n        let path = request.path\n        var isDirectory: ObjCBool = false\n        guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) else {\n            throw GRPCStatus(code: .notFound, message: \"copy: path not found '\\(path)'\")\n        }\n        let isArchive = isDirectory.boolValue\n\n        // Determine total size for single files.\n        var totalSize: UInt64 = 0\n        if !isArchive {\n            let attrs = try FileManager.default.attributesOfItem(atPath: path)\n            if let size = attrs[.size] as? UInt64 {\n                totalSize = size\n            }\n        }\n\n        // Send metadata response BEFORE connecting to vsock, so host knows what to expect.\n        try await responseStream.send(\n            .with {\n                $0.status = .metadata\n                $0.isArchive = isArchive\n                $0.totalSize = totalSize\n            })\n\n        // Connect to the host's vsock port and dispatch blocking I/O onto the thread pool.\n        let vsockType = VsockType(port: request.vsockPort, cid: VsockType.hostCID)\n        let sock = try Socket(type: vsockType, closeOnDeinit: false)\n        try sock.connect()\n\n        try await blockingPool.runIfActive { [self] in\n            defer { try? sock.close() }\n\n            if isArchive {\n                let fileURL = URL(fileURLWithPath: path)\n                let writer = try ArchiveWriter(configuration: .init(format: .pax, filter: .gzip))\n                try writer.open(fileDescriptor: sock.fileDescriptor)\n                try writer.archiveDirectory(fileURL)\n                try writer.finishEncoding()\n            } else {\n                let srcFd = open(path, O_RDONLY)\n                guard srcFd != -1 else {\n                    throw GRPCStatus(\n                        code: .internalError,\n                        message: \"copy: failed to open '\\(path)': \\(swiftErrno(\"open\"))\"\n                    )\n                }\n                defer { close(srcFd) }\n\n                var buf = [UInt8](repeating: 0, count: Self.copyChunkSize)\n                while true {\n                    let n = read(srcFd, &buf, buf.count)\n                    if n == 0 { break }\n                    guard n > 0 else {\n                        throw GRPCStatus(\n                            code: .internalError,\n                            message: \"copy: read error: \\(swiftErrno(\"read\"))\"\n                        )\n                    }\n                    var written = 0\n                    while written < n {\n                        let w = buf.withUnsafeBytes { ptr in\n                            write(sock.fileDescriptor, ptr.baseAddress! + written, n - written)\n                        }\n                        guard w > 0 else {\n                            throw GRPCStatus(\n                                code: .internalError,\n                                message: \"copy: vsock write error: \\(swiftErrno(\"write\"))\"\n                            )\n                        }\n                        written += w\n                    }\n                }\n            }\n        }\n\n        log.debug(\n            \"copy: copyOut complete\",\n            metadata: [\n                \"path\": \"\\(path)\",\n                \"isArchive\": \"\\(isArchive)\",\n            ])\n\n        // Send completion response after vsock data transfer is done.\n        try await responseStream.send(.with { $0.status = .complete })\n    }\n\n    func mount(request: Com_Apple_Containerization_Sandbox_V3_MountRequest, context: GRPC.GRPCAsyncServerCallContext)\n        async throws -> Com_Apple_Containerization_Sandbox_V3_MountResponse\n    {\n        log.debug(\n            \"mount\",\n            metadata: [\n                \"type\": \"\\(request.type)\",\n                \"source\": \"\\(request.source)\",\n                \"destination\": \"\\(request.destination)\",\n            ])\n\n        do {\n            let mnt = ContainerizationOS.Mount(\n                type: request.type,\n                source: request.source,\n                target: request.destination,\n                options: request.options\n            )\n\n            #if os(Linux)\n            try mnt.mount(createWithPerms: 0o755)\n            return .init()\n            #else\n            fatalError(\"mount not supported on platform\")\n            #endif\n        } catch {\n            log.error(\n                \"mount\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n            throw GRPCStatus(code: .internalError, message: \"mount: \\(error)\")\n        }\n    }\n\n    func umount(request: Com_Apple_Containerization_Sandbox_V3_UmountRequest, context: GRPC.GRPCAsyncServerCallContext)\n        async throws -> Com_Apple_Containerization_Sandbox_V3_UmountResponse\n    {\n        log.debug(\n            \"umount\",\n            metadata: [\n                \"path\": \"\\(request.path)\",\n                \"flags\": \"\\(request.flags)\",\n            ])\n\n        #if os(Linux)\n        // Best effort EBUSY handle.\n        for _ in 0...50 {\n            let result = _umount(request.path, request.flags)\n            if result == -1 {\n                if errno == EBUSY {\n                    try await Task.sleep(for: .milliseconds(10))\n                    continue\n                }\n                let error = swiftErrno(\"umount\")\n\n                log.error(\n                    \"umount\",\n                    metadata: [\n                        \"error\": \"\\(error)\"\n                    ])\n                throw GRPCStatus(code: .invalidArgument, message: \"umount: \\(error)\")\n            }\n            break\n        }\n        return .init()\n        #else\n        fatalError(\"umount not supported on platform\")\n        #endif\n    }\n\n    func setenv(request: Com_Apple_Containerization_Sandbox_V3_SetenvRequest, context: GRPC.GRPCAsyncServerCallContext)\n        async throws -> Com_Apple_Containerization_Sandbox_V3_SetenvResponse\n    {\n        log.debug(\n            \"setenv\",\n            metadata: [\n                \"key\": \"\\(request.key)\",\n                \"value\": \"\\(request.value)\",\n            ])\n\n        guard _setenv(request.key, request.value, 1) == 0 else {\n            let error = swiftErrno(\"setenv\")\n\n            log.error(\n                \"setEnv\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n\n            throw GRPCStatus(code: .invalidArgument, message: \"setenv: \\(error)\")\n        }\n        return .init()\n    }\n\n    func getenv(request: Com_Apple_Containerization_Sandbox_V3_GetenvRequest, context: GRPC.GRPCAsyncServerCallContext)\n        async throws -> Com_Apple_Containerization_Sandbox_V3_GetenvResponse\n    {\n        log.debug(\n            \"getenv\",\n            metadata: [\n                \"key\": \"\\(request.key)\"\n            ])\n\n        let env = ProcessInfo.processInfo.environment[request.key]\n        return .with {\n            if let env {\n                $0.value = env\n            }\n        }\n    }\n\n    func createProcess(\n        request: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest, context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse {\n        log.debug(\n            \"createProcess\",\n            metadata: [\n                \"id\": \"\\(request.id)\",\n                \"containerID\": \"\\(request.containerID)\",\n                \"stdin\": \"Port: \\(request.stdin)\",\n                \"stdout\": \"Port: \\(request.stdout)\",\n                \"stderr\": \"Port: \\(request.stderr)\",\n            ])\n\n        do {\n            if !request.hasContainerID {\n                throw ContainerizationError(\n                    .invalidArgument,\n                    message: \"processes in the root of the vm not implemented\"\n                )\n            }\n\n            var ociSpec = try JSONDecoder().decode(\n                ContainerizationOCI.Spec.self,\n                from: request.configuration\n            )\n\n            try ociAlterations(id: request.id, ociSpec: &ociSpec)\n\n            guard let process = ociSpec.process else {\n                throw ContainerizationError(\n                    .invalidArgument,\n                    message: \"oci runtime spec missing process configuration\"\n                )\n            }\n\n            let stdioPorts = HostStdio(\n                stdin: request.hasStdin ? request.stdin : nil,\n                stdout: request.hasStdout ? request.stdout : nil,\n                stderr: request.hasStderr ? request.stderr : nil,\n                terminal: process.terminal\n            )\n\n            // This is an exec.\n            if let container = await self.state.containers[request.containerID] {\n                try await container.createExec(\n                    id: request.id,\n                    stdio: stdioPorts,\n                    process: process\n                )\n            } else {\n                // We need to make our new fangled container.\n                // The process ID must match the container ID for this.\n                guard request.id == request.containerID else {\n                    throw ContainerizationError(\n                        .invalidArgument,\n                        message: \"init process id must match container id\"\n                    )\n                }\n\n                // Write the etc/hostname file in the container rootfs since some init-systems\n                // depend on it.\n                let hostname = ociSpec.hostname\n                if let root = ociSpec.root, !hostname.isEmpty {\n                    let etc = URL(fileURLWithPath: root.path).appendingPathComponent(\"etc\")\n                    try FileManager.default.createDirectory(atPath: etc.path, withIntermediateDirectories: true)\n                    let hostnamePath = etc.appendingPathComponent(\"hostname\")\n                    try hostname.write(toFile: hostnamePath.path, atomically: true, encoding: .utf8)\n                }\n\n                let ctr = try await ManagedContainer(\n                    id: request.id,\n                    stdio: stdioPorts,\n                    spec: ociSpec,\n                    ociRuntimePath: request.hasOciRuntimePath ? request.ociRuntimePath : nil,\n                    log: self.log\n                )\n                try await self.state.add(container: ctr)\n            }\n\n            return .init()\n        } catch let err as ContainerizationError {\n            log.error(\n                \"createProcess\",\n                metadata: [\n                    \"id\": \"\\(request.id)\",\n                    \"containerID\": \"\\(request.containerID)\",\n                    \"error\": \"\\(err)\",\n                ])\n            throw err.toGRPCStatus(operation: \"createProcess: failed to create process\")\n        } catch {\n            log.error(\n                \"createProcess\",\n                metadata: [\n                    \"id\": \"\\(request.id)\",\n                    \"containerID\": \"\\(request.containerID)\",\n                    \"error\": \"\\(error)\",\n                ])\n            throw GRPCStatus(code: .internalError, message: \"createProcess: failed to create process: \\(error)\")\n        }\n    }\n\n    func killProcess(\n        request: Com_Apple_Containerization_Sandbox_V3_KillProcessRequest,\n        context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_KillProcessResponse {\n        log.debug(\n            \"killProcess\",\n            metadata: [\n                \"id\": \"\\(request.id)\",\n                \"containerID\": \"\\(request.containerID)\",\n                \"signal\": \"\\(request.signal)\",\n            ])\n\n        do {\n            if !request.hasContainerID {\n                throw ContainerizationError(\n                    .invalidArgument,\n                    message: \"processes in the root of the vm not implemented\"\n                )\n            }\n\n            let ctr = try await self.state.get(container: request.containerID)\n            try await ctr.kill(execID: request.id, request.signal)\n\n            return .init()\n        } catch let err as ContainerizationError {\n            log.error(\n                \"killProcess\",\n                metadata: [\n                    \"id\": \"\\(request.id)\",\n                    \"containerID\": \"\\(request.containerID)\",\n                    \"error\": \"\\(err)\",\n                ])\n            throw err.toGRPCStatus(operation: \"killProcess: failed to kill process\")\n        } catch {\n            log.error(\n                \"killProcess\",\n                metadata: [\n                    \"id\": \"\\(request.id)\",\n                    \"containerID\": \"\\(request.containerID)\",\n                    \"error\": \"\\(error)\",\n                ])\n            throw GRPCStatus(code: .internalError, message: \"killProcess: failed to kill process: \\(error)\")\n        }\n    }\n\n    func deleteProcess(\n        request: Com_Apple_Containerization_Sandbox_V3_DeleteProcessRequest, context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_DeleteProcessResponse {\n        log.debug(\n            \"deleteProcess\",\n            metadata: [\n                \"id\": \"\\(request.id)\",\n                \"containerID\": \"\\(request.containerID)\",\n            ])\n\n        do {\n            if !request.hasContainerID {\n                throw ContainerizationError(\n                    .invalidArgument,\n                    message: \"processes in the root of the vm not implemented\"\n                )\n            }\n\n            let ctr = try await self.state.get(container: request.containerID)\n\n            // Are we trying to delete the container itself?\n            if request.id == request.containerID {\n                try await ctr.delete()\n                try await state.remove(container: request.id)\n            } else {\n                // Or just a single exec.\n                try await ctr.deleteExec(id: request.id)\n            }\n\n            return .init()\n        } catch let err as ContainerizationError {\n            log.error(\n                \"deleteProcess\",\n                metadata: [\n                    \"id\": \"\\(request.id)\",\n                    \"containerID\": \"\\(request.containerID)\",\n                    \"error\": \"\\(err)\",\n                ])\n            throw err.toGRPCStatus(operation: \"deleteProcess: failed to delete process\")\n        } catch {\n            log.error(\n                \"deleteProcess\",\n                metadata: [\n                    \"id\": \"\\(request.id)\",\n                    \"containerID\": \"\\(request.containerID)\",\n                    \"error\": \"\\(error)\",\n                ])\n            throw GRPCStatus(code: .internalError, message: \"deleteProcess: failed to delete process: \\(error)\")\n        }\n    }\n\n    func startProcess(\n        request: Com_Apple_Containerization_Sandbox_V3_StartProcessRequest, context: GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_StartProcessResponse {\n        log.debug(\n            \"startProcess\",\n            metadata: [\n                \"id\": \"\\(request.id)\",\n                \"containerID\": \"\\(request.containerID)\",\n            ])\n\n        do {\n            if !request.hasContainerID {\n                throw ContainerizationError(\n                    .invalidArgument,\n                    message: \"processes in the root of the vm not implemented\"\n                )\n            }\n\n            let ctr = try await self.state.get(container: request.containerID)\n            let pid = try await ctr.start(execID: request.id)\n\n            return .with {\n                $0.pid = pid\n            }\n        } catch let err as ContainerizationError {\n            log.error(\n                \"startProcess\",\n                metadata: [\n                    \"id\": \"\\(request.id)\",\n                    \"containerID\": \"\\(request.containerID)\",\n                    \"error\": \"\\(err)\",\n                ])\n            throw err.toGRPCStatus(operation: \"startProcess: failed to start process\")\n        } catch {\n            log.error(\n                \"startProcess\",\n                metadata: [\n                    \"id\": \"\\(request.id)\",\n                    \"containerID\": \"\\(request.containerID)\",\n                    \"error\": \"\\(error)\",\n                ])\n            throw GRPCStatus(\n                code: .internalError,\n                message: \"startProcess: failed to start process: \\(error)\"\n            )\n        }\n    }\n\n    func resizeProcess(\n        request: Com_Apple_Containerization_Sandbox_V3_ResizeProcessRequest, context: GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_ResizeProcessResponse {\n        log.debug(\n            \"resizeProcess\",\n            metadata: [\n                \"id\": \"\\(request.id)\",\n                \"containerID\": \"\\(request.containerID)\",\n            ])\n\n        do {\n            if !request.hasContainerID {\n                throw ContainerizationError(\n                    .invalidArgument,\n                    message: \"processes in the root of the vm not implemented\"\n                )\n            }\n\n            let ctr = try await self.state.get(container: request.containerID)\n            let size = Terminal.Size(\n                width: UInt16(request.columns),\n                height: UInt16(request.rows)\n            )\n            try await ctr.resize(execID: request.id, size: size)\n        } catch let err as ContainerizationError {\n            log.error(\n                \"resizeProcess\",\n                metadata: [\n                    \"id\": \"\\(request.id)\",\n                    \"containerID\": \"\\(request.containerID)\",\n                    \"error\": \"\\(err)\",\n                ])\n            throw err.toGRPCStatus(operation: \"resizeProcess: failed to resize process\")\n        } catch {\n            log.error(\n                \"resizeProcess\",\n                metadata: [\n                    \"id\": \"\\(request.id)\",\n                    \"containerID\": \"\\(request.containerID)\",\n                    \"error\": \"\\(error)\",\n                ])\n            throw GRPCStatus(\n                code: .internalError,\n                message: \"resizeProcess: failed to resize process: \\(error)\"\n            )\n        }\n\n        return .init()\n    }\n\n    func waitProcess(\n        request: Com_Apple_Containerization_Sandbox_V3_WaitProcessRequest, context: GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_WaitProcessResponse {\n        log.debug(\n            \"waitProcess\",\n            metadata: [\n                \"id\": \"\\(request.id)\",\n                \"containerID\": \"\\(request.containerID)\",\n            ])\n\n        do {\n            if !request.hasContainerID {\n                throw ContainerizationError(\n                    .invalidArgument,\n                    message: \"processes in the root of the vm not implemented\"\n                )\n            }\n\n            let ctr = try await self.state.get(container: request.containerID)\n            let exitStatus = try await ctr.wait(execID: request.id)\n\n            return .with {\n                $0.exitCode = exitStatus.exitCode\n                $0.exitedAt = Google_Protobuf_Timestamp(date: exitStatus.exitedAt)\n            }\n        } catch let err as ContainerizationError {\n            log.error(\n                \"waitProcess\",\n                metadata: [\n                    \"id\": \"\\(request.id)\",\n                    \"containerID\": \"\\(request.containerID)\",\n                    \"error\": \"\\(err)\",\n                ])\n            throw err.toGRPCStatus(operation: \"waitProcess: failed to wait on process\")\n        } catch {\n            log.error(\n                \"waitProcess\",\n                metadata: [\n                    \"id\": \"\\(request.id)\",\n                    \"containerID\": \"\\(request.containerID)\",\n                    \"error\": \"\\(error)\",\n                ])\n            throw GRPCStatus(\n                code: .internalError,\n                message: \"waitProcess: failed to wait on process: \\(error)\"\n            )\n        }\n    }\n\n    func closeProcessStdin(\n        request: Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinRequest, context: GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_CloseProcessStdinResponse {\n        log.debug(\n            \"closeProcessStdin\",\n            metadata: [\n                \"id\": \"\\(request.id)\",\n                \"containerID\": \"\\(request.containerID)\",\n            ])\n\n        do {\n            if !request.hasContainerID {\n                throw ContainerizationError(\n                    .invalidArgument,\n                    message: \"processes in the root of the vm not implemented\"\n                )\n            }\n\n            let ctr = try await self.state.get(container: request.containerID)\n\n            try await ctr.closeStdin(execID: request.id)\n\n            return .init()\n        } catch let err as ContainerizationError {\n            log.error(\n                \"closeProcessStdin\",\n                metadata: [\n                    \"id\": \"\\(request.id)\",\n                    \"containerID\": \"\\(request.containerID)\",\n                    \"error\": \"\\(err)\",\n                ])\n            throw err.toGRPCStatus(operation: \"closeProcessStdin: failed to close process stdin\")\n        } catch {\n            log.error(\n                \"closeProcessStdin\",\n                metadata: [\n                    \"id\": \"\\(request.id)\",\n                    \"containerID\": \"\\(request.containerID)\",\n                    \"error\": \"\\(error)\",\n                ])\n            throw GRPCStatus(\n                code: .internalError,\n                message: \"closeProcessStdin: failed to close process stdin: \\(error)\"\n            )\n        }\n    }\n\n    func ipLinkSet(\n        request: Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest, context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_IpLinkSetResponse {\n        log.debug(\n            \"ipLinkSet\",\n            metadata: [\n                \"interface\": \"\\(request.interface)\",\n                \"up\": \"\\(request.up)\",\n            ])\n\n        do {\n            let socket = try DefaultNetlinkSocket()\n            let session = NetlinkSession(socket: socket, log: log)\n            let mtuValue: UInt32? = request.hasMtu ? request.mtu : nil\n            try session.linkSet(interface: request.interface, up: request.up, mtu: mtuValue)\n        } catch {\n            log.error(\n                \"ipLinkSet\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n            throw GRPCStatus(code: .internalError, message: \"ip-link-set: \\(error)\")\n        }\n\n        return .init()\n    }\n\n    func ipAddrAdd(\n        request: Com_Apple_Containerization_Sandbox_V3_IpAddrAddRequest, context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_IpAddrAddResponse {\n        log.debug(\n            \"ipAddrAdd\",\n            metadata: [\n                \"interface\": \"\\(request.interface)\",\n                \"ipv4Address\": \"\\(request.ipv4Address)\",\n            ])\n\n        do {\n            let socket = try DefaultNetlinkSocket()\n            let session = NetlinkSession(socket: socket, log: log)\n            let ipv4Address = try CIDRv4(request.ipv4Address)\n            try session.addressAdd(interface: request.interface, ipv4Address: ipv4Address)\n        } catch {\n            log.error(\n                \"ipAddrAdd\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n            throw GRPCStatus(code: .internalError, message: \"failed to set IP address on interface \\(request.interface): \\(error)\")\n        }\n\n        return .init()\n    }\n\n    func ipRouteAddLink(\n        request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkRequest, context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_IpRouteAddLinkResponse {\n        log.debug(\n            \"ipRouteAddLink\",\n            metadata: [\n                \"interface\": \"\\(request.interface)\",\n                \"dstIpv4Addr\": \"\\(request.dstIpv4Addr)\",\n                \"srcIpv4Addr\": \"\\(request.srcIpv4Addr)\",\n            ])\n\n        do {\n            let socket = try DefaultNetlinkSocket()\n            let session = NetlinkSession(socket: socket, log: log)\n            let dstIpv4Addr = try CIDRv4(request.dstIpv4Addr)\n            let srcIpv4Addr = request.srcIpv4Addr.isEmpty ? nil : try IPv4Address(request.srcIpv4Addr)\n            try session.routeAdd(\n                interface: request.interface,\n                dstIpv4Addr: dstIpv4Addr,\n                srcIpv4Addr: srcIpv4Addr\n            )\n        } catch {\n            log.error(\n                \"ipRouteAddLink\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n            throw GRPCStatus(code: .internalError, message: \"ip-route-add-link: \\(error)\")\n        }\n\n        return .init()\n    }\n\n    func ipRouteAddDefault(\n        request: Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultRequest,\n        context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_IpRouteAddDefaultResponse {\n        log.debug(\n            \"ipRouteAddDefault\",\n            metadata: [\n                \"interface\": \"\\(request.interface)\",\n                \"ipv4Gateway\": \"\\(request.ipv4Gateway)\",\n            ])\n\n        do {\n            let socket = try DefaultNetlinkSocket()\n            let session = NetlinkSession(socket: socket, log: log)\n            let ipv4Gateway = try IPv4Address(request.ipv4Gateway)\n            try session.routeAddDefault(interface: request.interface, ipv4Gateway: ipv4Gateway)\n        } catch {\n            log.error(\n                \"ipRouteAddDefault\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n            throw GRPCStatus(code: .internalError, message: \"failed to set default gateway on interface \\(request.interface): \\(error)\")\n        }\n\n        return .init()\n    }\n\n    func configureDns(\n        request: Com_Apple_Containerization_Sandbox_V3_ConfigureDnsRequest,\n        context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_ConfigureDnsResponse {\n        let domain = request.hasDomain ? request.domain : nil\n        log.debug(\n            \"configureDns\",\n            metadata: [\n                \"location\": \"\\(request.location)\",\n                \"nameservers\": \"\\(request.nameservers)\",\n                \"domain\": \"\\(domain ?? \"\")\",\n                \"searchDomains\": \"\\(request.searchDomains)\",\n                \"options\": \"\\(request.options)\",\n            ])\n\n        do {\n            let etc = URL(fileURLWithPath: request.location).appendingPathComponent(\"etc\")\n            try FileManager.default.createDirectory(atPath: etc.path, withIntermediateDirectories: true)\n            let resolvConf = etc.appendingPathComponent(\"resolv.conf\")\n            let config = DNS(\n                nameservers: request.nameservers,\n                domain: domain,\n                searchDomains: request.searchDomains,\n                options: request.options\n            )\n            let text = config.resolvConf\n            log.debug(\"writing to path \\(resolvConf.path) \\(text)\")\n            try text.write(toFile: resolvConf.path, atomically: true, encoding: .utf8)\n            log.debug(\"wrote resolver configuration\", metadata: [\"path\": \"\\(resolvConf.path)\"])\n        } catch {\n            log.error(\n                \"configureDns\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n            throw GRPCStatus(code: .internalError, message: \"failed to configure DNS at location \\(request.location): \\(error)\")\n        }\n\n        return .init()\n    }\n\n    func configureHosts(\n        request: Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest,\n        context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_ConfigureHostsResponse {\n        log.debug(\n            \"configureHosts\",\n            metadata: [\n                \"location\": \"\\(request.location)\"\n            ])\n\n        do {\n            let etc = URL(fileURLWithPath: request.location).appendingPathComponent(\"etc\")\n            try FileManager.default.createDirectory(atPath: etc.path, withIntermediateDirectories: true)\n            let hostsPath = etc.appendingPathComponent(\"hosts\")\n\n            let config = request.toCZHosts()\n            let text = config.hostsFile\n            try text.write(toFile: hostsPath.path, atomically: true, encoding: .utf8)\n\n            log.debug(\"wrote /etc/hosts configuration\", metadata: [\"path\": \"\\(hostsPath.path)\"])\n        } catch {\n            log.error(\n                \"configureHosts\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n            throw GRPCStatus(code: .internalError, message: \"configureHosts: \\(error)\")\n        }\n\n        return .init()\n    }\n\n    func containerStatistics(\n        request: Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsRequest,\n        context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_ContainerStatisticsResponse {\n        log.debug(\n            \"containerStatistics\",\n            metadata: [\n                \"container_ids\": \"\\(request.containerIds)\",\n                \"categories\": \"\\(request.categories)\",\n            ])\n\n        do {\n            // Parse requested categories (empty = all)\n            let categories = Set(request.categories)\n            let wantAll = categories.isEmpty\n            let wantProcess = wantAll || categories.contains(.process)\n            let wantMemory = wantAll || categories.contains(.memory)\n            let wantCPU = wantAll || categories.contains(.cpu)\n            let wantBlockIO = wantAll || categories.contains(.blockIo)\n            let wantNetwork = wantAll || categories.contains(.network)\n            let wantMemoryEvents = wantAll || categories.contains(.memoryEvents)\n\n            // Get all network interfaces (skip loopback) only if needed\n            let interfaces = wantNetwork ? try getNetworkInterfaces() : []\n\n            // Get containers to query\n            let containerIDs: [String]\n            if request.containerIds.isEmpty {\n                containerIDs = await Array(state.containers.keys)\n            } else {\n                containerIDs = request.containerIds\n            }\n\n            var containerStats: [Com_Apple_Containerization_Sandbox_V3_ContainerStats] = []\n\n            for containerID in containerIDs {\n                let container = try await state.get(container: containerID)\n\n                // Only fetch cgroup stats if needed\n                let cgStats: Cgroup2Stats?\n                if wantProcess || wantMemory || wantCPU || wantBlockIO {\n                    cgStats = try await container.stats()\n                } else {\n                    cgStats = nil\n                }\n\n                // Get network stats only if requested\n                var networkStats: [Com_Apple_Containerization_Sandbox_V3_NetworkStats] = []\n                if wantNetwork {\n                    let socket = try DefaultNetlinkSocket()\n                    let session = NetlinkSession(socket: socket, log: log)\n                    for interface in interfaces {\n                        let responses = try session.linkGet(interface: interface, includeStats: true)\n                        if responses.count == 1, let stats = try responses[0].getStatistics() {\n                            networkStats.append(\n                                .with {\n                                    $0.interface = interface\n                                    $0.receivedPackets = stats.rxPackets\n                                    $0.transmittedPackets = stats.txPackets\n                                    $0.receivedBytes = stats.rxBytes\n                                    $0.transmittedBytes = stats.txBytes\n                                    $0.receivedErrors = stats.rxErrors\n                                    $0.transmittedErrors = stats.txErrors\n                                })\n                        }\n                    }\n                }\n\n                // Get memory events only if requested\n                var memoryEvents: MemoryEvents?\n                if wantMemoryEvents {\n                    memoryEvents = try await container.getMemoryEvents()\n                }\n\n                containerStats.append(\n                    mapStatsToProto(\n                        containerID: containerID,\n                        cgStats: cgStats,\n                        networkStats: networkStats,\n                        memoryEvents: memoryEvents,\n                        wantProcess: wantProcess,\n                        wantMemory: wantMemory,\n                        wantCPU: wantCPU,\n                        wantBlockIO: wantBlockIO,\n                        wantNetwork: wantNetwork,\n                        wantMemoryEvents: wantMemoryEvents\n                    )\n                )\n            }\n\n            return .with {\n                $0.containers = containerStats\n            }\n        } catch {\n            log.error(\n                \"containerStatistics\",\n                metadata: [\n                    \"error\": \"\\(error)\"\n                ])\n            throw GRPCStatus(code: .internalError, message: \"containerStatistics: \\(error)\")\n        }\n    }\n\n    private func swiftErrno(_ msg: Logger.Message) -> POSIXError {\n        let error = POSIXError(.init(rawValue: errno)!)\n        log.error(\n            msg,\n            metadata: [\n                \"error\": \"\\(error)\"\n            ])\n        return error\n    }\n\n    // NOTE: This is just crummy. It works because today the assumption is\n    // every NIC in the root net namespace is for the container(s), but if we\n    // ever supported individual containers having their own NICs/IPs then this\n    // logic needs to change. We only create ethernet devices today too, so that's\n    // what this filters for as well.\n    private func getNetworkInterfaces() throws -> [String] {\n        let netPath = URL(filePath: \"/sys/class/net\")\n        let interfaces = try FileManager.default.contentsOfDirectory(\n            at: netPath,\n            includingPropertiesForKeys: nil\n        )\n        return\n            interfaces\n            .map { $0.lastPathComponent }\n            .filter { $0.hasPrefix(\"eth\") }\n    }\n\n    private func mapStatsToProto(\n        containerID: String,\n        cgStats: Cgroup2Stats?,\n        networkStats: [Com_Apple_Containerization_Sandbox_V3_NetworkStats],\n        memoryEvents: MemoryEvents?,\n        wantProcess: Bool,\n        wantMemory: Bool,\n        wantCPU: Bool,\n        wantBlockIO: Bool,\n        wantNetwork: Bool,\n        wantMemoryEvents: Bool\n    ) -> Com_Apple_Containerization_Sandbox_V3_ContainerStats {\n        .with {\n            $0.containerID = containerID\n\n            if wantProcess, let pids = cgStats?.pids {\n                $0.process = .with {\n                    $0.current = pids.current\n                    $0.limit = pids.max ?? 0\n                }\n            }\n\n            if wantMemory, let memory = cgStats?.memory {\n                $0.memory = .with {\n                    $0.usageBytes = memory.usage\n                    $0.limitBytes = memory.usageLimit ?? 0\n                    $0.swapUsageBytes = memory.swapUsage ?? 0\n                    $0.swapLimitBytes = memory.swapLimit ?? 0\n                    $0.cacheBytes = memory.file\n                    $0.kernelStackBytes = memory.kernelStack\n                    $0.slabBytes = memory.slab\n                    $0.pageFaults = memory.pgfault\n                    $0.majorPageFaults = memory.pgmajfault\n                    $0.inactiveFile = memory.inactiveFile\n                    $0.anon = memory.anon\n                }\n            }\n\n            if wantCPU, let cpu = cgStats?.cpu {\n                $0.cpu = .with {\n                    $0.usageUsec = cpu.usageUsec\n                    $0.userUsec = cpu.userUsec\n                    $0.systemUsec = cpu.systemUsec\n                    $0.throttlingPeriods = cpu.nrPeriods\n                    $0.throttledPeriods = cpu.nrThrottled\n                    $0.throttledTimeUsec = cpu.throttledUsec\n                }\n            }\n\n            if wantBlockIO, let io = cgStats?.io {\n                $0.blockIo = .with {\n                    $0.devices = io.entries.map { entry in\n                        .with {\n                            $0.major = entry.major\n                            $0.minor = entry.minor\n                            $0.readBytes = entry.rbytes\n                            $0.writeBytes = entry.wbytes\n                            $0.readOperations = entry.rios\n                            $0.writeOperations = entry.wios\n                        }\n                    }\n                }\n            }\n\n            if wantNetwork {\n                $0.networks = networkStats\n            }\n\n            if wantMemoryEvents, let events = memoryEvents {\n                $0.memoryEvents = .with {\n                    $0.low = events.low\n                    $0.high = events.high\n                    $0.max = events.max\n                    $0.oom = events.oom\n                    $0.oomKill = events.oomKill\n                }\n            }\n        }\n    }\n\n    func sync(\n        request: Com_Apple_Containerization_Sandbox_V3_SyncRequest,\n        context: GRPC.GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_SyncResponse {\n        log.debug(\"sync\")\n\n        _sync()\n        return .init()\n    }\n\n    func kill(\n        request: Com_Apple_Containerization_Sandbox_V3_KillRequest,\n        context: GRPCAsyncServerCallContext\n    ) async throws -> Com_Apple_Containerization_Sandbox_V3_KillResponse {\n        log.debug(\n            \"kill\",\n            metadata: [\n                \"pid\": \"\\(request.pid)\",\n                \"signal\": \"\\(request.signal)\",\n            ])\n\n        let r = _kill(request.pid, request.signal)\n        return .with {\n            $0.result = r\n        }\n    }\n}\n\nextension Com_Apple_Containerization_Sandbox_V3_ConfigureHostsRequest {\n    func toCZHosts() -> Hosts {\n        let entries = self.entries.map {\n            Hosts.Entry(\n                ipAddress: $0.ipAddress,\n                hostnames: $0.hostnames,\n                comment: $0.hasComment ? $0.comment : nil\n            )\n        }\n        return Hosts(\n            entries: entries,\n            comment: self.hasComment ? self.comment : nil\n        )\n    }\n}\n\nextension Initd {\n    func ociAlterations(id: String, ociSpec: inout ContainerizationOCI.Spec) throws {\n        guard var process = ociSpec.process else {\n            throw ContainerizationError(\n                .invalidArgument,\n                message: \"runtime spec without process field present\"\n            )\n        }\n        guard let root = ociSpec.root else {\n            throw ContainerizationError(\n                .invalidArgument,\n                message: \"runtime spec without root field present\"\n            )\n        }\n\n        if ociSpec.linux!.cgroupsPath.isEmpty {\n            ociSpec.linux!.cgroupsPath = \"/container/\\(id)\"\n        }\n\n        if process.cwd.isEmpty {\n            process.cwd = \"/\"\n        }\n\n        // NOTE: The OCI runtime specs Username field is truthfully Windows exclusive, but we use this as a way\n        // to pass through the exact string representation of a username (or username:group, uid:group etc.) a client\n        // may have given us.\n        let username = process.user.username.isEmpty ? \"\\(process.user.uid):\\(process.user.gid)\" : process.user.username\n        let parsedUser = try User.getExecUser(\n            userString: username,\n            passwdPath: URL(filePath: root.path).appending(path: \"etc/passwd\"),\n            groupPath: URL(filePath: root.path).appending(path: \"etc/group\")\n        )\n        process.user.uid = parsedUser.uid\n        process.user.gid = parsedUser.gid\n        process.user.additionalGids.append(contentsOf: parsedUser.sgids)\n        process.user.additionalGids.append(process.user.gid)\n\n        var seenSuppGids = Set<UInt32>()\n        process.user.additionalGids = process.user.additionalGids.filter {\n            seenSuppGids.insert($0).inserted\n        }\n\n        if !process.env.contains(where: { $0.hasPrefix(\"PATH=\") }) {\n            process.env.append(\"PATH=\\(LinuxProcessConfiguration.defaultPath)\")\n        }\n\n        if !process.env.contains(where: { $0.hasPrefix(\"HOME=\") }) {\n            process.env.append(\"HOME=\\(parsedUser.home)\")\n        }\n\n        // Defensive programming a tad, but ensure we have TERM set if\n        // the client requested a pty.\n        if process.terminal {\n            let termEnv = \"TERM=\"\n            if !process.env.contains(where: { $0.hasPrefix(termEnv) }) {\n                process.env.append(\"TERM=xterm\")\n            }\n        }\n\n        ociSpec.process = process\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/Server.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport Foundation\nimport GRPC\nimport Logging\nimport NIOCore\nimport NIOPosix\n\nfinal class Initd: Sendable {\n    actor State {\n        var containers: [String: ManagedContainer] = [:]\n        var proxies: [String: VsockProxy] = [:]\n\n        func get(container id: String) throws -> ManagedContainer {\n            guard let ctr = self.containers[id] else {\n                throw ContainerizationError(\n                    .notFound,\n                    message: \"container \\(id) not found\"\n                )\n            }\n            return ctr\n        }\n\n        func add(container: ManagedContainer) throws {\n            guard containers[container.id] == nil else {\n                throw ContainerizationError(\n                    .exists,\n                    message: \"container \\(container.id) already exists\"\n                )\n            }\n            containers[container.id] = container\n        }\n\n        func add(proxy: VsockProxy) throws {\n            guard proxies[proxy.id] == nil else {\n                throw ContainerizationError(\n                    .exists,\n                    message: \"proxy \\(proxy.id) already exists\"\n                )\n            }\n            proxies[proxy.id] = proxy\n        }\n\n        func remove(proxy id: String) throws -> VsockProxy {\n            guard let proxy = proxies.removeValue(forKey: id) else {\n                throw ContainerizationError(\n                    .notFound,\n                    message: \"proxy \\(id) does not exist\"\n                )\n            }\n            return proxy\n        }\n\n        func remove(container id: String) throws {\n            guard let _ = containers.removeValue(forKey: id) else {\n                throw ContainerizationError(\n                    .notFound,\n                    message: \"container \\(id) does not exist\"\n                )\n            }\n        }\n    }\n\n    let log: Logger\n    let state: State\n    let group: EventLoopGroup\n    let blockingPool: NIOThreadPool\n\n    init(log: Logger, group: EventLoopGroup, blockingPool: NIOThreadPool) {\n        self.log = log\n        self.group = group\n        self.blockingPool = blockingPool\n        self.state = State()\n    }\n\n    func serve(port: Int) async throws {\n        try await withThrowingTaskGroup(of: Void.self) { group in\n            log.debug(\"starting process supervisor\")\n\n            ProcessSupervisor.default.setLog(self.log)\n            ProcessSupervisor.default.ready()\n\n            log.info(\n                \"booting gRPC server on vsock\",\n                metadata: [\n                    \"port\": \"\\(port)\"\n                ])\n            let server = try await Server.start(\n                configuration: .default(\n                    target: .vsockAddress(.init(cid: .any, port: .init(port))),\n                    eventLoopGroup: self.group,\n                    serviceProviders: [self])\n            ).get()\n            log.info(\n                \"gRPC API serving on vsock\",\n                metadata: [\n                    \"port\": \"\\(port)\"\n                ])\n\n            group.addTask {\n                try await server.onClose.get()\n            }\n            try await group.next()\n            log.info(\"closing gRPC server\")\n            group.cancelAll()\n        }\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/StandardIO.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationError\nimport ContainerizationOS\nimport Foundation\nimport Logging\nimport Synchronization\n\nfinal class StandardIO: ManagedProcess.IO & Sendable {\n    private struct State {\n        var stdin: IOPair?\n        var stdout: IOPair?\n        var stderr: IOPair?\n\n        var stdinPipe: Pipe?\n        var stdoutPipe: Pipe?\n        var stderrPipe: Pipe?\n    }\n\n    private let log: Logger?\n    private let hostStdio: HostStdio\n    private let state: Mutex<State>\n\n    init(\n        stdio: HostStdio,\n        log: Logger?\n    ) {\n        self.hostStdio = stdio\n        self.log = log\n        self.state = Mutex(State())\n    }\n\n    // NOP\n    func attach(pid: Int32, fd: Int32) throws {}\n\n    func start(process: inout Command) throws {\n        try self.state.withLock {\n            if let stdinPort = self.hostStdio.stdin {\n                let inPipe = Pipe()\n                process.stdin = inPipe.fileHandleForReading\n                $0.stdinPipe = inPipe\n\n                let type = VsockType(\n                    port: stdinPort,\n                    cid: VsockType.hostCID\n                )\n                let stdinSocket = try Socket(type: type, closeOnDeinit: false)\n                try stdinSocket.connect()\n\n                let pair = IOPair(\n                    readFrom: stdinSocket,\n                    writeTo: inPipe.fileHandleForWriting,\n                    reason: \"StandardIO stdin\",\n                    logger: log\n                )\n                $0.stdin = pair\n\n                try pair.relay()\n            }\n\n            if let stdoutPort = self.hostStdio.stdout {\n                let outPipe = Pipe()\n                process.stdout = outPipe.fileHandleForWriting\n                $0.stdoutPipe = outPipe\n\n                let type = VsockType(\n                    port: stdoutPort,\n                    cid: VsockType.hostCID\n                )\n                let stdoutSocket = try Socket(type: type, closeOnDeinit: false)\n                try stdoutSocket.connect()\n\n                let pair = IOPair(\n                    readFrom: outPipe.fileHandleForReading,\n                    writeTo: stdoutSocket,\n                    reason: \"StandardIO stdout\",\n                    logger: log\n                )\n                $0.stdout = pair\n\n                try pair.relay()\n            }\n\n            if let stderrPort = self.hostStdio.stderr {\n                let errPipe = Pipe()\n                process.stderr = errPipe.fileHandleForWriting\n                $0.stderrPipe = errPipe\n\n                let type = VsockType(\n                    port: stderrPort,\n                    cid: VsockType.hostCID\n                )\n                let stderrSocket = try Socket(type: type, closeOnDeinit: false)\n                try stderrSocket.connect()\n\n                let pair = IOPair(\n                    readFrom: errPipe.fileHandleForReading,\n                    writeTo: stderrSocket,\n                    reason: \"StandardIO stderr\",\n                    logger: log\n                )\n                $0.stderr = pair\n\n                try pair.relay()\n            }\n        }\n    }\n\n    func resize(size: Terminal.Size) throws {\n        throw ContainerizationError(.unsupported, message: \"resize not supported\")\n    }\n\n    func close() throws {\n        self.state.withLock {\n            if let stdin = $0.stdin {\n                stdin.close()\n                $0.stdin = nil\n            }\n\n            if let stdout = $0.stdout {\n                stdout.close()\n                $0.stdout = nil\n            }\n\n            if let stderr = $0.stderr {\n                stderr.close()\n                $0.stderr = nil\n            }\n        }\n    }\n\n    func closeStdin() throws {\n        self.state.withLock {\n            if let stdin = $0.stdin {\n                stdin.close()\n                $0.stdin = nil\n            }\n        }\n    }\n\n    func closeAfterExec() throws {\n        try self.state.withLock {\n            if let stdin = $0.stdinPipe {\n                try stdin.fileHandleForReading.close()\n                $0.stdinPipe = nil\n            }\n            if let stdout = $0.stdoutPipe {\n                try stdout.fileHandleForWriting.close()\n                $0.stdoutPipe = nil\n            }\n            if let stderr = $0.stderrPipe {\n                try stderr.fileHandleForWriting.close()\n                $0.stderrPipe = nil\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/TerminalIO.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationOS\nimport Foundation\nimport LCShim\nimport Logging\nimport Synchronization\n\nfinal class TerminalIO: ManagedProcess.IO & Sendable {\n    private struct State {\n        var stdinSocket: Socket?\n        var stdoutSocket: Socket?\n\n        var stdin: IOPair?\n        var stdout: IOPair?\n        var parent: Terminal?\n    }\n\n    private let log: Logger?\n    private let hostStdio: HostStdio\n    private let state: Mutex<State>\n\n    init(\n        stdio: HostStdio,\n        log: Logger?\n    ) throws {\n        self.hostStdio = stdio\n        self.log = log\n        self.state = Mutex(State())\n    }\n\n    func resize(size: Terminal.Size) throws {\n        try self.state.withLock {\n            if let parent = $0.parent {\n                try parent.resize(size: size)\n            }\n        }\n    }\n\n    func start(process: inout Command) throws {\n        try self.state.withLock {\n            process.stdin = nil\n            process.stdout = nil\n            process.stderr = nil\n\n            if let stdinPort = self.hostStdio.stdin {\n                let type = VsockType(\n                    port: stdinPort,\n                    cid: VsockType.hostCID\n                )\n                let stdinSocket = try Socket(type: type, closeOnDeinit: false)\n                try stdinSocket.connect()\n                $0.stdinSocket = stdinSocket\n            }\n\n            if let stdoutPort = self.hostStdio.stdout {\n                let type = VsockType(\n                    port: stdoutPort,\n                    cid: VsockType.hostCID\n                )\n                let stdoutSocket = try Socket(type: type, closeOnDeinit: false)\n                try stdoutSocket.connect()\n                $0.stdoutSocket = stdoutSocket\n            }\n        }\n    }\n\n    func attach(pid: Int32, fd: Int32) throws {\n        try self.state.withLock {\n            let containerFd = CZ_pidfd_open(pid, 0)\n            guard containerFd != -1 else {\n                throw POSIXError.fromErrno()\n            }\n            defer { Foundation.close(Int32(containerFd)) }\n\n            let hostFd = CZ_pidfd_getfd(containerFd, fd, 0)\n            guard hostFd != -1 else {\n                throw POSIXError.fromErrno()\n            }\n\n            let term = try Terminal(descriptor: Int32(hostFd), setInitState: false)\n            $0.parent = term\n\n            if let stdinSocket = $0.stdinSocket {\n                let pair = IOPair(\n                    readFrom: stdinSocket,\n                    writeTo: term,\n                    reason: \"TerminalIO stdin\",\n                    logger: log\n                )\n                try pair.relay(ignoreHup: true)\n                $0.stdin = pair\n            }\n\n            if let stdoutSocket = $0.stdoutSocket {\n                let pair = IOPair(\n                    readFrom: term,\n                    writeTo: stdoutSocket,\n                    reason: \"TerminalIO stdout\",\n                    logger: log\n                )\n                try pair.relay(ignoreHup: true)\n                $0.stdout = pair\n            }\n        }\n    }\n\n    func close() throws {\n        self.state.withLock {\n            if let stdin = $0.stdin {\n                stdin.close()\n                $0.stdin = nil\n            }\n            if let stdout = $0.stdout {\n                stdout.close()\n                $0.stdout = nil\n            }\n            $0.parent = nil\n        }\n    }\n\n    // NOP\n    func closeAfterExec() throws {}\n\n    func closeStdin() throws {\n        self.state.withLock {\n            if let stdin = $0.stdin {\n                stdin.close()\n                $0.stdin = nil\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "vminitd/Sources/vminitd/VsockProxy.swift",
    "content": "//===----------------------------------------------------------------------===//\n// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//===----------------------------------------------------------------------===//\n\nimport ContainerizationIO\nimport ContainerizationOS\nimport Foundation\nimport Logging\n\nactor VsockProxy {\n    enum Action {\n        case listen\n        case dial\n    }\n\n    private enum SocketType {\n        case unix\n        case vsock\n    }\n\n    let id: String\n\n    private let path: URL\n    private let action: Action\n    private let port: UInt32\n    private let udsPerms: UInt32?\n    private let log: Logger?\n\n    private var listener: Socket?\n    private var task: Task<(), Never>?\n\n    init(\n        id: String,\n        action: Action,\n        port: UInt32,\n        path: URL,\n        udsPerms: UInt32?,\n        log: Logger? = nil\n    ) {\n        self.id = id\n        self.action = action\n        self.port = port\n        self.path = path\n        self.udsPerms = udsPerms\n        self.log = log\n    }\n}\n\nextension VsockProxy {\n    func start() throws {\n        guard listener == nil else {\n            return\n        }\n\n        log?.debug(\n            \"starting proxy\",\n            metadata: [\n                \"vport\": \"\\(port)\",\n                \"uds\": \"\\(path)\",\n                \"action\": \"\\(action)\",\n            ])\n\n        switch action {\n        case .dial:\n            try dialHost()\n        case .listen:\n            try dialGuest()\n        }\n    }\n\n    func close() throws {\n        guard let listener else {\n            return\n        }\n\n        log?.debug(\n            \"stopping proxy\",\n            metadata: [\n                \"vport\": \"\\(port)\",\n                \"uds\": \"\\(path)\",\n                \"action\": \"\\(action)\",\n            ])\n\n        try listener.close()\n\n        if action == .dial {\n            let fm = FileManager.default\n            if fm.fileExists(atPath: path.path) {\n                try fm.removeItem(at: path)\n            }\n        }\n\n        task?.cancel()\n        self.listener = nil\n    }\n\n    private func dialHost() throws {\n        let fm = FileManager.default\n\n        let parentDir = path.deletingLastPathComponent()\n        try fm.createDirectory(\n            at: parentDir,\n            withIntermediateDirectories: true\n        )\n\n        let type = try UnixType(\n            path: path.path,\n            perms: udsPerms,\n            unlinkExisting: true\n        )\n        let oldMask = umask(0)\n        defer { umask(oldMask) }\n        let uds = try Socket(type: type)\n        try uds.listen()\n        listener = uds\n\n        try acceptLoop(socketType: .unix)\n    }\n\n    private func dialGuest() throws {\n        let type = VsockType(\n            port: port,\n            cid: VsockType.anyCID\n        )\n        let vsock = try Socket(type: type)\n        try vsock.listen()\n        listener = vsock\n\n        try acceptLoop(socketType: .vsock)\n    }\n\n    private func acceptLoop(socketType: SocketType) throws {\n        guard let listener else {\n            return\n        }\n\n        let stream = try listener.acceptStream()\n        let task = Task {\n            do {\n                for try await conn in stream {\n                    Task {\n                        log?.debug(\n                            \"accepting connection\",\n                            metadata: [\n                                \"vport\": \"\\(port)\",\n                                \"uds\": \"\\(path)\",\n                                \"action\": \"\\(action)\",\n                                \"socketType\": \"\\(socketType)\",\n                            ])\n                        do {\n                            try await handleConn(\n                                conn: conn,\n                                connType: socketType\n                            )\n                        } catch {\n                            self.log?.error(\"failed to handle connection: \\(error)\")\n                        }\n                    }\n                }\n            } catch {\n                self.log?.error(\"failed to accept connection: \\(error)\")\n            }\n        }\n        self.task = task\n    }\n\n    private func handleConn(\n        conn: ContainerizationOS.Socket,\n        connType: SocketType\n    ) async throws {\n        try await withCheckedThrowingContinuation { (c: CheckedContinuation<Void, Error>) in\n            do {\n                // `relayTo` isn't used concurrently.\n                nonisolated(unsafe) var relayTo: ContainerizationOS.Socket\n\n                switch connType {\n                case .unix:\n                    let type = VsockType(\n                        port: port,\n                        cid: VsockType.hostCID\n                    )\n                    relayTo = try Socket(\n                        type: type,\n                        closeOnDeinit: false\n                    )\n                case .vsock:\n                    let type = try UnixType(path: path.path)\n                    relayTo = try Socket(\n                        type: type,\n                        closeOnDeinit: false\n                    )\n                }\n\n                try relayTo.connect()\n\n                // `clientFile` isn't used concurrently.\n                nonisolated(unsafe) var clientFile = OSFile.SpliceFile(fd: conn.fileDescriptor)\n                nonisolated(unsafe) var eofFromClient = false\n                // `serverFile` isn't used concurrently.\n                nonisolated(unsafe) var serverFile = OSFile.SpliceFile(fd: relayTo.fileDescriptor)\n                nonisolated(unsafe) var eofFromServer = false\n\n                // clean up when any of these conditions apply:\n                //   - the client has completely hung up or errored\n                //   - the server has completely hung up or errored\n                //   - both the client and server have half closed via:\n                //     - read hangup on epoll\n                //     - EOF on splice\n                let cleanup = { @Sendable [log, port, path, action] in\n                    log?.debug(\n                        \"cleaning up\",\n                        metadata: [\n                            \"vport\": \"\\(port)\",\n                            \"uds\": \"\\(path)\",\n                            \"action\": \"\\(action)\",\n                            \"eofFromClient\": \"\\(eofFromClient)\",\n                            \"eofFromServer\": \"\\(eofFromServer)\",\n                            \"clientFd\": \"\\(clientFile.fileDescriptor)\",\n                            \"serverFd\": \"\\(serverFile.fileDescriptor)\",\n                        ]\n                    )\n\n                    do {\n                        try ProcessSupervisor.default.poller.delete(clientFile.fileDescriptor)\n                        try ProcessSupervisor.default.poller.delete(serverFile.fileDescriptor)\n                        try conn.close()\n                        try relayTo.close()\n                    } catch {\n                        self.log?.error(\"Failed to clean up vsock proxy: \\(error)\")\n                    }\n                    c.resume()\n                }\n\n                try! ProcessSupervisor.default.poller.add(clientFile.fileDescriptor, mask: EPOLLIN | EPOLLOUT) { mask in\n                    if mask.readyToRead && !eofFromClient {\n                        let (fromEof, toEof) = Self.transferData(\n                            fromFile: &clientFile,\n                            toFile: &serverFile,\n                            description: \"readyToRead:toServer\",\n                            log: self.log\n                        )\n                        eofFromClient = eofFromClient || fromEof\n                        eofFromServer = eofFromServer || toEof\n                    }\n\n                    if mask.readyToWrite && !eofFromServer {\n                        let (fromEof, toEof) = Self.transferData(\n                            fromFile: &serverFile,\n                            toFile: &clientFile,\n                            description: \"readyToWrite:toClient\",\n                            log: self.log\n                        )\n                        eofFromClient = eofFromClient || toEof\n                        eofFromServer = eofFromServer || fromEof\n                    }\n\n                    if mask.isHangup {\n                        eofFromClient = true\n                        eofFromServer = true\n                    } else if mask.isRhangup && !eofFromClient {\n                        // half close, shut down client to server transfer\n                        // we should see no more EPOLLIN events on the client fd\n                        // and no more EPOLLOUT events on the server fd\n                        eofFromClient = true\n                        if shutdown(serverFile.fileDescriptor, SHUT_WR) != 0 {\n                            self.log?.warning(\n                                \"failed to shut down client reads\",\n                                metadata: [\n                                    \"vport\": \"\\(self.port)\",\n                                    \"uds\": \"\\(self.path)\",\n                                    \"errno\": \"\\(errno)\",\n                                    \"eofFromClient\": \"\\(eofFromClient)\",\n                                    \"eofFromServer\": \"\\(eofFromServer)\",\n                                    \"clientFd\": \"\\(clientFile.fileDescriptor)\",\n                                    \"serverFd\": \"\\(serverFile.fileDescriptor)\",\n                                ]\n                            )\n                        }\n                    }\n\n                    if eofFromClient && eofFromServer {\n                        return cleanup()\n                    }\n                }\n\n                try! ProcessSupervisor.default.poller.add(serverFile.fileDescriptor, mask: EPOLLIN | EPOLLOUT) { mask in\n                    if mask.readyToRead && !eofFromServer {\n                        let (fromEof, toEof) = Self.transferData(\n                            fromFile: &serverFile,\n                            toFile: &clientFile,\n                            description: \"readyToRead:toClient\",\n                            log: self.log\n                        )\n                        eofFromClient = eofFromClient || toEof\n                        eofFromServer = eofFromServer || fromEof\n                    }\n\n                    if mask.readyToWrite && !eofFromClient {\n                        let (fromEof, toEof) = Self.transferData(\n                            fromFile: &clientFile,\n                            toFile: &serverFile,\n                            description: \"readyToWrite:toServer\",\n                            log: self.log\n                        )\n                        eofFromClient = eofFromClient || fromEof\n                        eofFromServer = eofFromServer || toEof\n                    }\n\n                    if mask.isHangup {\n                        eofFromClient = true\n                        eofFromServer = true\n                    } else if mask.isRhangup && !eofFromServer {\n                        // half close, shut down server to client transfer\n                        // we should see no more EPOLLIN events on the server fd\n                        // and no more EPOLLOUT events on the client fd\n                        eofFromServer = true\n                        if shutdown(clientFile.fileDescriptor, SHUT_WR) != 0 {\n                            self.log?.warning(\n                                \"failed to shut down server reads\",\n                                metadata: [\n                                    \"vport\": \"\\(self.port)\",\n                                    \"uds\": \"\\(self.path)\",\n                                    \"errno\": \"\\(errno)\",\n                                    \"eofFromClient\": \"\\(eofFromClient)\",\n                                    \"eofFromServer\": \"\\(eofFromServer)\",\n                                    \"clientFd\": \"\\(clientFile.fileDescriptor)\",\n                                    \"serverFd\": \"\\(serverFile.fileDescriptor)\",\n                                ]\n                            )\n                        }\n                    }\n\n                    if eofFromClient && eofFromServer {\n                        return cleanup()\n                    }\n                }\n            } catch {\n                c.resume(throwing: error)\n            }\n        }\n    }\n\n    private static func transferData(\n        fromFile: inout OSFile.SpliceFile,\n        toFile: inout OSFile.SpliceFile,\n        description: String,\n        log: Logger?\n    ) -> (Bool, Bool) {\n        do {\n            let (readBytes, writeBytes, action) = try OSFile.splice(from: &fromFile, to: &toFile)\n            log?.trace(\n                \"transferred data\",\n                metadata: [\n                    \"description\": \"\\(description)\",\n                    \"action\": \"\\(action)\",\n                    \"readBytes\": \"\\(readBytes)\",\n                    \"writeBytes\": \"\\(writeBytes)\",\n                    \"fromFd\": \"\\(fromFile.fileDescriptor)\",\n                    \"toFd\": \"\\(toFile.fileDescriptor)\",\n                ]\n            )\n            if action == .eof {\n                // half close, shut down client to server transfer\n                // we should see no more EPOLLIN events on the client fd\n                // and no more EPOLLOUT events on the server fd\n                if shutdown(toFile.fileDescriptor, SHUT_WR) != 0 {\n                    log?.warning(\n                        \"failed to shut down reads\",\n                        metadata: [\n                            \"description\": \"\\(description)\",\n                            \"errno\": \"\\(errno)\",\n                            \"action\": \"\\(action)\",\n                            \"readBytes\": \"\\(readBytes)\",\n                            \"writeBytes\": \"\\(writeBytes)\",\n                            \"fromFd\": \"\\(fromFile.fileDescriptor)\",\n                            \"toFd\": \"\\(toFile.fileDescriptor)\",\n                        ]\n                    )\n                }\n                return (true, false)\n            } else if action == .brokenPipe {\n                return (true, true)\n            }\n            return (false, false)\n        } catch {\n            return (true, true)\n        }\n    }\n}\n"
  }
]