windows-development-sandbox 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config.example.json +10 -0
- package/dist/app/context.d.ts +8 -0
- package/dist/app/context.js +12 -0
- package/dist/app/features/defs.d.ts +322 -0
- package/dist/app/features/defs.js +28 -0
- package/dist/app/features/sandbox/index.d.ts +2 -0
- package/dist/app/features/sandbox/index.js +3 -0
- package/dist/app/features/sandbox/sandbox.def.d.ts +193 -0
- package/dist/app/features/sandbox/sandbox.def.js +245 -0
- package/dist/app/features/sandbox/sandbox.run.d.ts +72 -0
- package/dist/app/features/sandbox/sandbox.run.js +98 -0
- package/dist/app/features/sessions/index.d.ts +2 -0
- package/dist/app/features/sessions/index.js +3 -0
- package/dist/app/features/sessions/sessions.def.d.ts +141 -0
- package/dist/app/features/sessions/sessions.def.js +176 -0
- package/dist/app/features/sessions/sessions.run.d.ts +39 -0
- package/dist/app/features/sessions/sessions.run.js +58 -0
- package/dist/{cli/menu/settings/categories.d.ts → app/features/settings/categories.def.d.ts} +2 -0
- package/dist/{cli/menu/settings/categories.js → app/features/settings/categories.def.js} +24 -5
- package/dist/app/features/settings/index.d.ts +5 -0
- package/dist/app/features/settings/index.js +6 -0
- package/dist/{cli/menu/settings/config-file.d.ts → app/features/settings/settings-file.d.ts} +1 -1
- package/dist/{cli/menu/settings/config-file.js → app/features/settings/settings-file.js} +1 -1
- package/dist/app/features/settings/settings-rows.d.ts +35 -0
- package/dist/app/features/settings/settings-rows.js +94 -0
- package/dist/app/features/settings/settings.def.d.ts +157 -0
- package/dist/app/features/settings/settings.def.js +179 -0
- package/dist/app/features/settings/settings.run.d.ts +67 -0
- package/dist/app/features/settings/settings.run.js +84 -0
- package/dist/app/kernel/define.d.ts +143 -0
- package/dist/app/kernel/define.js +42 -0
- package/dist/app/kernel/errors.d.ts +34 -0
- package/dist/app/kernel/errors.js +45 -0
- package/dist/app/kernel/fields.d.ts +70 -0
- package/dist/app/kernel/fields.js +243 -0
- package/dist/app/kernel/index.d.ts +8 -0
- package/dist/app/kernel/index.js +10 -0
- package/dist/app/kernel/io.d.ts +193 -0
- package/dist/app/kernel/io.js +126 -0
- package/dist/app/kernel/rpc.d.ts +156 -0
- package/dist/app/kernel/rpc.js +47 -0
- package/dist/app/kernel/text.d.ts +6 -0
- package/dist/app/kernel/text.js +57 -0
- package/dist/app/kernel/view.d.ts +146 -0
- package/dist/app/kernel/view.js +28 -0
- package/dist/app/registry.d.ts +10 -0
- package/dist/app/registry.js +50 -0
- package/dist/cli/args.d.ts +5 -0
- package/dist/cli/args.js +6 -0
- package/dist/cli/commands/engine.d.ts +2 -0
- package/dist/cli/commands/engine.js +60 -0
- package/dist/cli/commands/index.d.ts +1 -1
- package/dist/cli/commands/index.js +1 -1
- package/dist/cli/commands/launch/launch.js +47 -7
- package/dist/cli/commands/launch/prepare.js +3 -3
- package/dist/cli/commands/launch/running.js +2 -1
- package/dist/cli/commands/launch/summary.d.ts +2 -0
- package/dist/cli/commands/maintenance/config-share.d.ts +12 -0
- package/dist/cli/commands/maintenance/config-share.js +80 -0
- package/dist/cli/commands/maintenance/config.js +8 -82
- package/dist/cli/commands/maintenance/logs.js +14 -0
- package/dist/cli/commands/registry.js +3 -7
- package/dist/cli/commands/repair/checks/contained.d.ts +2 -0
- package/dist/cli/commands/repair/checks/contained.js +139 -0
- package/dist/cli/commands/repair/checks/hcs.d.ts +7 -0
- package/dist/cli/commands/repair/checks/hcs.js +28 -0
- package/dist/cli/commands/repair/checks/index.js +4 -0
- package/dist/cli/commands/repair/checks/pc.js +32 -1
- package/dist/cli/commands/repair/checks/sandbox.js +16 -1
- package/dist/cli/commands/repair/checks/vpn.js +34 -9
- package/dist/cli/commands/repair/env.js +2 -1
- package/dist/cli/commands/sessions/index.d.ts +0 -4
- package/dist/cli/commands/sessions/index.js +3 -7
- package/dist/cli/commands/shared-use.d.ts +5 -2
- package/dist/cli/commands/shared-use.js +11 -8
- package/dist/cli/commands/storage/contained.d.ts +3 -0
- package/dist/cli/commands/storage/contained.js +179 -0
- package/dist/cli/commands/storage/offer.js +7 -1
- package/dist/cli/commands/storage/storage.js +10 -2
- package/dist/cli/commands/user-config.js +1 -1
- package/dist/cli/commands/vpn.js +19 -3
- package/dist/cli/commands/web.js +2 -0
- package/dist/cli/index.js +2 -1
- package/dist/cli/manifest.d.ts +72 -104
- package/dist/cli/manifest.js +64 -39
- package/dist/cli/menu/add-agent.d.ts +1 -1
- package/dist/cli/menu/add-agent.js +1 -1
- package/dist/cli/menu/apps.d.ts +7 -1
- package/dist/cli/menu/apps.js +36 -0
- package/dist/cli/menu/command-fields.d.ts +1 -1
- package/dist/cli/menu/command-form.js +2 -2
- package/dist/cli/menu/io-menu.d.ts +29 -0
- package/dist/cli/menu/io-menu.js +126 -0
- package/dist/cli/menu/live.d.ts +27 -0
- package/dist/cli/menu/live.js +69 -0
- package/dist/cli/menu/memory.d.ts +19 -0
- package/dist/cli/menu/memory.js +53 -0
- package/dist/cli/menu/menu.d.ts +10 -1
- package/dist/cli/menu/menu.js +27 -9
- package/dist/cli/menu/pages/agents-page.d.ts +3 -1
- package/dist/cli/menu/pages/agents-page.js +12 -5
- package/dist/cli/menu/pages/apps-page.d.ts +3 -1
- package/dist/cli/menu/pages/apps-page.js +19 -2
- package/dist/cli/menu/pages/list-page.d.ts +27 -0
- package/dist/cli/menu/pages/list-page.js +109 -7
- package/dist/cli/menu/pages/maintenance-page.d.ts +0 -5
- package/dist/cli/menu/pages/maintenance-page.js +17 -19
- package/dist/cli/menu/pages/sandbox-page.d.ts +4 -6
- package/dist/cli/menu/pages/sandbox-page.js +26 -118
- package/dist/cli/menu/pages/scopes.d.ts +1 -1
- package/dist/cli/menu/pages/sessions-page.d.ts +5 -11
- package/dist/cli/menu/pages/sessions-page.js +19 -88
- package/dist/cli/menu/pages/settings-page.d.ts +2 -10
- package/dist/cli/menu/pages/settings-page.js +64 -94
- package/dist/cli/menu/render-view.d.ts +28 -0
- package/dist/cli/menu/render-view.js +112 -0
- package/dist/cli/menu/settings/{path-kind.d.ts → ask-path.d.ts} +1 -7
- package/dist/cli/menu/settings/ask-path.js +14 -0
- package/dist/cli/menu/settings/collections/collection-editor.d.ts +1 -1
- package/dist/cli/menu/settings/collections/string-list-editor.d.ts +1 -2
- package/dist/cli/menu/settings/collections/string-list-editor.js +1 -1
- package/dist/cli/menu/settings/draft.d.ts +1 -1
- package/dist/cli/menu/settings/format.d.ts +1 -3
- package/dist/cli/menu/settings/format.js +6 -36
- package/dist/cli/menu/settings/index.d.ts +1 -3
- package/dist/cli/menu/settings/index.js +3 -3
- package/dist/cli/menu/settings/object-editor.d.ts +1 -1
- package/dist/cli/menu/settings/share.d.ts +36 -0
- package/dist/cli/menu/settings/share.js +131 -0
- package/dist/cli/menu/settings/value-editor.d.ts +5 -1
- package/dist/cli/menu/settings/value-editor.js +57 -54
- package/dist/cli/menu/several.js +1 -1
- package/dist/cli/menu/share-settings.d.ts +21 -0
- package/dist/cli/menu/share-settings.js +112 -0
- package/dist/cli/menu/shell.d.ts +21 -5
- package/dist/cli/menu/shell.js +31 -6
- package/dist/cli/menu/views.d.ts +7 -0
- package/dist/cli/menu/views.js +11 -0
- package/dist/cli/menu/vpn-login.js +8 -0
- package/dist/cli/operations/handler.d.ts +6 -0
- package/dist/cli/operations/handler.js +61 -0
- package/dist/cli/operations/index.d.ts +4 -0
- package/dist/cli/operations/index.js +5 -0
- package/dist/cli/operations/input.d.ts +6 -0
- package/dist/cli/operations/input.js +37 -0
- package/dist/cli/operations/io-cli.d.ts +7 -0
- package/dist/cli/operations/io-cli.js +62 -0
- package/dist/cli/operations/spec.d.ts +18 -0
- package/dist/cli/operations/spec.js +108 -0
- package/dist/cli/output/colors.d.ts +2 -2
- package/dist/cli/output/colors.js +2 -11
- package/dist/cli/output/index.d.ts +1 -0
- package/dist/cli/output/index.js +1 -0
- package/dist/cli/output/print.d.ts +3 -1
- package/dist/cli/output/print.js +7 -3
- package/dist/cli/output/progress.d.ts +15 -0
- package/dist/cli/output/progress.js +44 -0
- package/dist/cli/output/render-view.d.ts +4 -0
- package/dist/cli/output/render-view.js +63 -0
- package/dist/cli/services/agent-commands-service.d.ts +2 -0
- package/dist/cli/services/agent-commands-service.js +11 -2
- package/dist/cli/services/logs-service.js +4 -3
- package/dist/cli/tui/app.d.ts +6 -1
- package/dist/cli/tui/app.js +26 -4
- package/dist/cli/tui/list/list-screen.d.ts +14 -0
- package/dist/cli/tui/list/list-screen.js +64 -36
- package/dist/cli/tui/list/list-types.d.ts +2 -0
- package/dist/cli/tui/terminal.d.ts +7 -0
- package/dist/cli/tui/terminal.js +30 -10
- package/dist/core/config/defaults.js +8 -0
- package/dist/core/schema/config.d.ts +13 -0
- package/dist/core/schema/config.js +16 -1
- package/dist/core/schema/harness.d.ts +1 -0
- package/dist/core/schema/harness.js +5 -0
- package/dist/core/schema/sandbox.d.ts +6 -0
- package/dist/core/schema/sandbox.js +20 -3
- package/dist/core/schema/vpn.d.ts +2 -1
- package/dist/core/schema/vpn.js +3 -3
- package/dist/core/types.d.ts +23 -5
- package/dist/core/util/fs.d.ts +2 -0
- package/dist/core/util/fs.js +9 -0
- package/dist/core/util/index.d.ts +1 -1
- package/dist/core/util/index.js +1 -1
- package/dist/guest/agents/agent-files.d.ts +5 -0
- package/dist/guest/agents/agent-files.js +4 -0
- package/dist/guest/agents/local-state.d.ts +2 -0
- package/dist/guest/agents/local-state.js +21 -1
- package/dist/guest/boot/boot.js +2 -1
- package/dist/guest/daemon/daemon.js +4 -0
- package/dist/guest/daemon/sessions.d.ts +5 -0
- package/dist/guest/daemon/sessions.js +17 -3
- package/dist/guest/entries/daemon.js +3 -0
- package/dist/guest/governor/decide.d.ts +61 -0
- package/dist/guest/governor/decide.js +182 -0
- package/dist/guest/governor/governor.d.ts +42 -0
- package/dist/guest/governor/governor.js +214 -0
- package/dist/guest/governor/index.d.ts +2 -0
- package/dist/guest/governor/index.js +2 -0
- package/dist/guest/runtime/csc.d.ts +1 -0
- package/dist/guest/runtime/csc.js +1 -0
- package/dist/guest/sessions/viewer.js +3 -1
- package/dist/guest/toolchains/native/rust-extras.js +4 -2
- package/dist/guest/vpn/vpn.js +69 -21
- package/dist/guest/vpn/wireguard.d.ts +32 -0
- package/dist/guest/vpn/wireguard.js +132 -0
- package/dist/host/agent-commands/commands.d.ts +4 -0
- package/dist/host/agent-commands/commands.js +31 -7
- package/dist/host/audit/log.d.ts +1 -1
- package/dist/host/channel/attach.d.ts +3 -1
- package/dist/host/channel/attach.js +5 -2
- package/dist/host/channel/channel.d.ts +2 -0
- package/dist/host/channel/channel.js +4 -0
- package/dist/host/engines/engine.d.ts +12 -0
- package/dist/host/engines/engine.js +1 -0
- package/dist/host/engines/hcs/check.d.ts +25 -0
- package/dist/host/engines/hcs/check.js +95 -0
- package/dist/host/engines/hcs/documents.d.ts +44 -0
- package/dist/host/engines/hcs/documents.js +155 -0
- package/dist/host/engines/hcs/engine.d.ts +39 -0
- package/dist/host/engines/hcs/engine.js +171 -0
- package/dist/host/engines/hcs/helper.d.ts +43 -0
- package/dist/host/engines/hcs/helper.js +173 -0
- package/dist/host/engines/hcs/images.d.ts +37 -0
- package/dist/host/engines/hcs/images.js +152 -0
- package/dist/host/engines/index.d.ts +7 -0
- package/dist/host/engines/index.js +5 -0
- package/dist/host/engines/registry.d.ts +16 -0
- package/dist/host/engines/registry.js +30 -0
- package/dist/host/engines/types.d.ts +58 -0
- package/dist/host/engines/types.js +1 -0
- package/dist/host/engines/windows-sandbox/engine.d.ts +16 -0
- package/dist/host/engines/windows-sandbox/engine.js +63 -0
- package/dist/host/engines/windows-sandbox/wsb-text.d.ts +2 -0
- package/dist/host/engines/windows-sandbox/wsb-text.js +29 -0
- package/dist/host/launch/generate.d.ts +5 -1
- package/dist/host/launch/generate.js +50 -8
- package/dist/host/launch/index.d.ts +3 -1
- package/dist/host/launch/index.js +3 -1
- package/dist/host/launch/launch-plan.d.ts +13 -0
- package/dist/host/launch/launch-plan.js +26 -0
- package/dist/host/launch/mappings.js +2 -1
- package/dist/host/launch/risk-notes.d.ts +10 -0
- package/dist/host/launch/risk-notes.js +18 -0
- package/dist/host/launch/session-file.d.ts +2 -0
- package/dist/host/launch/session-file.js +7 -1
- package/dist/host/launch/wsb-file.d.ts +28 -4
- package/dist/host/launch/wsb-file.js +61 -42
- package/dist/host/life/crash-report.d.ts +27 -1
- package/dist/host/life/crash-report.js +173 -7
- package/dist/host/life/host-memory.d.ts +40 -0
- package/dist/host/life/host-memory.js +137 -0
- package/dist/host/life/index.d.ts +2 -0
- package/dist/host/life/index.js +2 -0
- package/dist/host/life/stop-events.d.ts +2 -0
- package/dist/host/life/stop-events.js +20 -3
- package/dist/host/life/stop-warning.js +1 -1
- package/dist/host/life/watchdog.d.ts +54 -0
- package/dist/host/life/watchdog.js +108 -0
- package/dist/host/native.d.ts +3 -2
- package/dist/host/native.js +22 -3
- package/dist/host/profile/claude-login.js +3 -2
- package/dist/host/sandbox/contained-deps.d.ts +2 -0
- package/dist/host/sandbox/contained-deps.js +17 -0
- package/dist/host/sandbox/index.d.ts +2 -0
- package/dist/host/sandbox/index.js +2 -0
- package/dist/host/sandbox/platform.d.ts +2 -0
- package/dist/host/sandbox/platform.js +7 -7
- package/dist/host/sandbox/tasklist.d.ts +13 -0
- package/dist/host/sandbox/tasklist.js +47 -0
- package/dist/host/secrets/launch-secrets.js +2 -1
- package/dist/host/sharing/current.js +2 -6
- package/dist/host/storage/contained/check.d.ts +9 -0
- package/dist/host/storage/contained/check.js +37 -0
- package/dist/host/storage/contained/contained-storage.d.ts +66 -0
- package/dist/host/storage/contained/contained-storage.js +168 -0
- package/dist/host/storage/contained/disk-port.d.ts +43 -0
- package/dist/host/storage/contained/disk-port.js +122 -0
- package/dist/host/storage/contained/index.d.ts +9 -0
- package/dist/host/storage/contained/index.js +8 -0
- package/dist/host/storage/contained/layout.d.ts +28 -0
- package/dist/host/storage/contained/layout.js +45 -0
- package/dist/host/storage/contained/resolve.d.ts +4 -0
- package/dist/host/storage/contained/resolve.js +30 -0
- package/dist/host/storage/contained/setting.d.ts +1 -0
- package/dist/host/storage/contained/setting.js +5 -0
- package/dist/host/storage/contained/state.d.ts +89 -0
- package/dist/host/storage/contained/state.js +166 -0
- package/dist/host/storage/contained/transfer.d.ts +24 -0
- package/dist/host/storage/contained/transfer.js +121 -0
- package/dist/host/storage/index.d.ts +2 -0
- package/dist/host/storage/index.js +2 -0
- package/dist/host/storage/providers/contained.d.ts +15 -0
- package/dist/host/storage/providers/contained.js +39 -0
- package/dist/host/storage/providers/folder.d.ts +12 -0
- package/dist/host/storage/providers/folder.js +20 -0
- package/dist/host/storage/providers/index.d.ts +4 -0
- package/dist/host/storage/providers/index.js +3 -0
- package/dist/host/storage/providers/registry.d.ts +9 -0
- package/dist/host/storage/providers/registry.js +14 -0
- package/dist/host/storage/providers/types.d.ts +33 -0
- package/dist/host/storage/providers/types.js +1 -0
- package/dist/host/tools/conpty.js +3 -2
- package/dist/host/tools/windows-terminal.js +8 -7
- package/dist/host/vpn/index.d.ts +1 -1
- package/dist/host/vpn/index.js +1 -1
- package/dist/host/vpn/prepare.d.ts +2 -0
- package/dist/host/vpn/prepare.js +63 -2
- package/dist/host/web/browser.d.ts +2 -0
- package/dist/host/web/browser.js +14 -1
- package/dist/host/web/index.d.ts +3 -2
- package/dist/host/web/index.js +3 -2
- package/dist/host/web/rpc.d.ts +24 -0
- package/dist/host/web/rpc.js +149 -0
- package/dist/host/web/runner.d.ts +2 -0
- package/dist/host/web/runner.js +1 -2
- package/dist/host/web/server.d.ts +7 -0
- package/dist/host/web/server.js +23 -3
- package/dist/runtime/agent.js +69 -8
- package/dist/runtime/daemon.js +1058 -624
- package/dist/runtime/db.js +69 -8
- package/dist/runtime/main.js +624 -182
- package/dist/runtime/netproxy.js +69 -8
- package/dist/runtime/view.js +87 -13
- package/dist/selftest/agent-paths.test.js +11 -1
- package/dist/selftest/app.test.d.ts +2 -0
- package/dist/selftest/app.test.js +324 -0
- package/dist/selftest/channel.test.js +13 -0
- package/dist/selftest/cli.test.js +2 -1
- package/dist/selftest/config.test.js +59 -0
- package/dist/selftest/contained.test.d.ts +2 -0
- package/dist/selftest/contained.test.js +544 -0
- package/dist/selftest/credentials.test.js +1 -1
- package/dist/selftest/engine.test.d.ts +2 -0
- package/dist/selftest/engine.test.js +127 -0
- package/dist/selftest/hcs.test.d.ts +2 -0
- package/dist/selftest/hcs.test.js +482 -0
- package/dist/selftest/heavy-load.test.d.ts +2 -0
- package/dist/selftest/heavy-load.test.js +329 -0
- package/dist/selftest/index.js +12 -0
- package/dist/selftest/layout.test.js +12 -0
- package/dist/selftest/menu-perf.test.d.ts +2 -0
- package/dist/selftest/menu-perf.test.js +248 -0
- package/dist/selftest/menu-screens.js +4 -2
- package/dist/selftest/menu.test.js +36 -8
- package/dist/selftest/support/engine-cases.d.ts +8 -0
- package/dist/selftest/support/engine-cases.js +65 -0
- package/dist/selftest/support/engine-golden.d.ts +3 -0
- package/dist/selftest/support/engine-golden.js +66 -0
- package/dist/selftest/support/fixtures.d.ts +7 -1
- package/dist/selftest/support/hcs-probes.d.ts +17 -0
- package/dist/selftest/support/hcs-probes.js +41 -0
- package/dist/selftest/vpn.test.js +136 -3
- package/dist/selftest/web.test.js +101 -3
- package/dist/shared/channel/run-dir.d.ts +4 -1
- package/dist/shared/channel/run-dir.js +7 -1
- package/dist/shared/channel/schema.d.ts +35 -1
- package/dist/shared/channel/schema.js +27 -0
- package/dist/shared/channel/secrets.d.ts +1 -0
- package/dist/shared/channel/secrets.js +6 -0
- package/dist/shared/memory/index.d.ts +1 -0
- package/dist/shared/memory/index.js +1 -0
- package/dist/shared/memory/policy.d.ts +33 -0
- package/dist/shared/memory/policy.js +55 -0
- package/dist/shared/pty/output-filter.d.ts +14 -1
- package/dist/shared/pty/output-filter.js +22 -4
- package/dist/shared/pty/terminal.d.ts +4 -1
- package/dist/shared/pty/terminal.js +4 -2
- package/dist/shared/system.d.ts +1 -0
- package/dist/shared/system.js +1 -0
- package/dist/shared/vpn/download.d.ts +16 -0
- package/dist/shared/vpn/download.js +16 -0
- package/dist/shared/vpn/wireguard.d.ts +51 -0
- package/dist/shared/vpn/wireguard.js +227 -0
- package/dist/sync/sync-cycle.js +5 -5
- package/dist/sync/watcher.d.ts +13 -0
- package/dist/sync/watcher.js +92 -3
- package/dist/web/index.html +4 -3
- package/dist/web/main-NRRS5O6Z.js +157 -0
- package/dist/web/main-T5DMCHYV.css +1 -0
- package/native/csharp/HcsHost/AssemblyInfo.cs +14 -0
- package/native/csharp/HcsHost/Guard.cs +100 -0
- package/native/csharp/HcsHost/Hcs.cs +256 -0
- package/native/csharp/HcsHost/Json.cs +78 -0
- package/native/csharp/HcsHost/Program.cs +185 -0
- package/native/csharp/HcsHost/VirtualDisk.cs +95 -0
- package/native/csharp/HostTools/AssemblyInfo.cs +14 -0
- package/native/csharp/HostTools/MemoryStatus.cs +107 -0
- package/native/csharp/HostTools/Program.cs +58 -17
- package/native/scripts/check-contained.ps1 +28 -0
- package/native/scripts/contained-common.ps1 +241 -0
- package/native/scripts/dismount-contained.ps1 +66 -0
- package/native/scripts/memory-status.ps1 +34 -0
- package/native/scripts/mount-contained.ps1 +139 -0
- package/native/scripts/run-elevated.ps1 +17 -4
- package/native/scripts/stop-events.ps1 +23 -8
- package/package.json +9 -2
- package/sandbox/csharp/MemGov.cs +398 -0
- package/sandbox/csharp/PtyHost.cs +111 -5
- package/schemas/config.schema.json +60 -6
- package/schemas/sandbox-config.schema.json +3 -3
- package/dist/cli/commands/sessions/control.d.ts +0 -4
- package/dist/cli/commands/sessions/control.js +0 -66
- package/dist/cli/commands/sessions/list.d.ts +0 -2
- package/dist/cli/commands/sessions/list.js +0 -48
- package/dist/cli/commands/sessions/status.d.ts +0 -4
- package/dist/cli/commands/sessions/status.js +0 -114
- package/dist/cli/commands/sessions/window.d.ts +0 -3
- package/dist/cli/commands/sessions/window.js +0 -18
- package/dist/cli/menu/settings/path-kind.js +0 -44
- package/dist/web/main-CXESL347.js +0 -45
- package/dist/web/main-VLR7DVQ4.css +0 -1
- /package/dist/{cli/menu/settings → app/kernel}/schema-node.d.ts +0 -0
- /package/dist/{cli/menu/settings → app/kernel}/schema-node.js +0 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// This PC's memory right now (HostTools memory-status): physical RAM and the commit limit (RAM plus paging files) with
|
|
2
|
+
// what is left of each. The commit limit is what a sandbox's memory is charged against, so it matters as much as RAM.
|
|
3
|
+
// C# 5.
|
|
4
|
+
using System;
|
|
5
|
+
using System.ComponentModel;
|
|
6
|
+
using System.Globalization;
|
|
7
|
+
using System.Runtime.InteropServices;
|
|
8
|
+
using Microsoft.Win32;
|
|
9
|
+
|
|
10
|
+
namespace AgentSandbox
|
|
11
|
+
{
|
|
12
|
+
public static class HostMemory
|
|
13
|
+
{
|
|
14
|
+
private const double BytesPerMb = 1024.0 * 1024.0;
|
|
15
|
+
|
|
16
|
+
// MEMORYSTATUSEX.
|
|
17
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
18
|
+
private struct MemoryStatus
|
|
19
|
+
{
|
|
20
|
+
public uint Length;
|
|
21
|
+
public uint MemoryLoad;
|
|
22
|
+
public ulong TotalPhys;
|
|
23
|
+
public ulong AvailPhys;
|
|
24
|
+
public ulong TotalPageFile;
|
|
25
|
+
public ulong AvailPageFile;
|
|
26
|
+
public ulong TotalVirtual;
|
|
27
|
+
public ulong AvailVirtual;
|
|
28
|
+
public ulong AvailExtendedVirtual;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private static class NativeMethods
|
|
32
|
+
{
|
|
33
|
+
[DllImport("kernel32.dll", SetLastError = true)]
|
|
34
|
+
public static extern bool GlobalMemoryStatusEx(ref MemoryStatus status);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// <summary>{"totalMb":..,"availableMb":..,"commitLimitMb":..,"commitAvailableMb":..}; throws Win32Exception on failure.</summary>
|
|
38
|
+
public static string Json()
|
|
39
|
+
{
|
|
40
|
+
MemoryStatus status = new MemoryStatus();
|
|
41
|
+
status.Length = (uint)Marshal.SizeOf(typeof(MemoryStatus));
|
|
42
|
+
if (!NativeMethods.GlobalMemoryStatusEx(ref status))
|
|
43
|
+
{
|
|
44
|
+
throw new Win32Exception(Marshal.GetLastWin32Error());
|
|
45
|
+
}
|
|
46
|
+
return "{\"totalMb\":" + Mb(status.TotalPhys) +
|
|
47
|
+
",\"availableMb\":" + Mb(status.AvailPhys) +
|
|
48
|
+
",\"commitLimitMb\":" + Mb(status.TotalPageFile) +
|
|
49
|
+
",\"commitAvailableMb\":" + Mb(status.AvailPageFile) +
|
|
50
|
+
"," + PageFileJson() + "}";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// <summary>
|
|
54
|
+
/// How this PC's paging file is set up, from the registry (read-only): "auto" (Windows manages it), "fixed"
|
|
55
|
+
/// (with the total of the maximum sizes) or "none". An unreadable setting is reported as "auto".
|
|
56
|
+
/// </summary>
|
|
57
|
+
private static string PageFileJson()
|
|
58
|
+
{
|
|
59
|
+
string kind = "auto";
|
|
60
|
+
long maxMb = 0;
|
|
61
|
+
try
|
|
62
|
+
{
|
|
63
|
+
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management"))
|
|
64
|
+
{
|
|
65
|
+
string[] files = key == null ? null : key.GetValue("PagingFiles") as string[];
|
|
66
|
+
if (files != null)
|
|
67
|
+
{
|
|
68
|
+
kind = "none";
|
|
69
|
+
foreach (string entry in files)
|
|
70
|
+
{
|
|
71
|
+
string[] parts = entry.Trim().Split(' ');
|
|
72
|
+
if (entry.Trim().Length == 0)
|
|
73
|
+
{
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
long initial = 0;
|
|
77
|
+
long max = 0;
|
|
78
|
+
if (parts.Length >= 3)
|
|
79
|
+
{
|
|
80
|
+
long.TryParse(parts[parts.Length - 2], NumberStyles.Integer, CultureInfo.InvariantCulture, out initial);
|
|
81
|
+
long.TryParse(parts[parts.Length - 1], NumberStyles.Integer, CultureInfo.InvariantCulture, out max);
|
|
82
|
+
}
|
|
83
|
+
if (entry.StartsWith("?:") || (initial == 0 && max == 0))
|
|
84
|
+
{
|
|
85
|
+
kind = "auto";
|
|
86
|
+
maxMb = 0;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
kind = "fixed";
|
|
90
|
+
maxMb += max;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (Exception)
|
|
96
|
+
{
|
|
97
|
+
// Not readable: the caller treats "auto" as "no warning".
|
|
98
|
+
}
|
|
99
|
+
return "\"pageFile\":\"" + kind + "\",\"pageFileMaxMb\":" + maxMb.ToString(CultureInfo.InvariantCulture);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private static string Mb(ulong bytes)
|
|
103
|
+
{
|
|
104
|
+
return Math.Floor(bytes / BytesPerMb).ToString("0", CultureInfo.InvariantCulture);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// HostTools.exe: what the host side asks of Windows without PowerShell. Built on first use by host/native.ts (csc).
|
|
2
2
|
// keep-awake --owner-pid N this PC stays awake until process N ends (sandbox.keepAwake)
|
|
3
3
|
// stop-events --since <iso> the Windows events since then that can end a sandbox, as JSON (host/life)
|
|
4
|
+
// memory-status this PC's RAM and commit (what a sandbox's memory is charged against), as JSON
|
|
4
5
|
// hide-window --wait S hides the sandbox window once it appears; prints the hidden handles as JSON
|
|
5
6
|
// show-window --handles a,b shows those windows (else any sandbox window); prints how many
|
|
6
7
|
// user-path --add|--remove D D first on, or off, your PATH; exit 3 when PATH would be too long
|
|
@@ -15,6 +16,7 @@ static class Program
|
|
|
15
16
|
{
|
|
16
17
|
private const int MaxEventsPerQuery = 100;
|
|
17
18
|
private const int MaxDetail = 390;
|
|
19
|
+
private const int MaxResourceDetail = 1500;
|
|
18
20
|
|
|
19
21
|
static int Main(string[] args)
|
|
20
22
|
{
|
|
@@ -39,6 +41,17 @@ static class Program
|
|
|
39
41
|
}
|
|
40
42
|
Console.Out.Write(StopEventsJson(since));
|
|
41
43
|
return 0;
|
|
44
|
+
case "memory-status":
|
|
45
|
+
try
|
|
46
|
+
{
|
|
47
|
+
Console.Out.Write(AgentSandbox.HostMemory.Json());
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
catch (Exception e)
|
|
51
|
+
{
|
|
52
|
+
Console.Error.WriteLine(e.Message);
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
42
55
|
case "hide-window":
|
|
43
56
|
int wait;
|
|
44
57
|
if (!options.TryGetValue("wait", out value) || !int.TryParse(value, out wait))
|
|
@@ -84,7 +97,7 @@ static class Program
|
|
|
84
97
|
|
|
85
98
|
static int Usage()
|
|
86
99
|
{
|
|
87
|
-
Console.Error.WriteLine("Usage: HostTools keep-awake --owner-pid N | stop-events --since <ISO time> | hide-window --wait S | show-window [--handles a,b] | user-path --add|--remove <folder>");
|
|
100
|
+
Console.Error.WriteLine("Usage: HostTools keep-awake --owner-pid N | stop-events --since <ISO time> | memory-status | hide-window --wait S | show-window [--handles a,b] | user-path --add|--remove <folder>");
|
|
88
101
|
return 2;
|
|
89
102
|
}
|
|
90
103
|
|
|
@@ -127,13 +140,20 @@ static class Program
|
|
|
127
140
|
new[] { "System", "*[System[(EventID=1074 or EventID=6008 or EventID=41 or EventID=42 or EventID=2004) and " + time + "]]" },
|
|
128
141
|
new[] { "System", "*[System[Provider[@Name='Microsoft-Windows-WER-SystemErrorReporting'] and EventID=1001 and " + time + "]]" },
|
|
129
142
|
new[] { "Microsoft-Windows-Hyper-V-Worker-Admin", "*[System[(EventID=18590 or EventID=40001) and " + time + "]]" },
|
|
143
|
+
// Hardware errors Windows reported (WHEA: CPU, memory, bus, disk).
|
|
144
|
+
new[] { "System", "*[System[Provider[@Name='Microsoft-Windows-WHEA-Logger'] and (EventID=17 or EventID=18 or EventID=19 or EventID=47) and " + time + "]]" },
|
|
145
|
+
// Any error or critical event of the Hyper-V worker and compute services (the VM's memory, start and stop).
|
|
146
|
+
new[] { "Microsoft-Windows-Hyper-V-Worker-Admin", "*[System[(Level=1 or Level=2) and " + time + "]]" },
|
|
147
|
+
new[] { "Microsoft-Windows-Hyper-V-Compute-Admin", "*[System[(Level=1 or Level=2) and " + time + "]]" },
|
|
148
|
+
// A crash report of the programs that hold the VM (kept only when it names one of them, see Detail).
|
|
149
|
+
new[] { "Application", "*[System[(EventID=1000 or EventID=1001) and " + time + "]]" },
|
|
130
150
|
};
|
|
131
151
|
List<string> items = new List<string>();
|
|
132
152
|
foreach (string[] query in queries)
|
|
133
153
|
{
|
|
134
154
|
try
|
|
135
155
|
{
|
|
136
|
-
ReadEvents(query[0], query[1], items);
|
|
156
|
+
ReadEvents(query[0], query[1], items, query[0] == "Application");
|
|
137
157
|
}
|
|
138
158
|
catch (EventLogException)
|
|
139
159
|
{
|
|
@@ -148,7 +168,7 @@ static class Program
|
|
|
148
168
|
}
|
|
149
169
|
|
|
150
170
|
/// <summary>The newest events first, at most 100 per query, as JSON objects { at, id, provider, process }.</summary>
|
|
151
|
-
static void ReadEvents(string log, string xpath, List<string> items)
|
|
171
|
+
static void ReadEvents(string log, string xpath, List<string> items, bool onlyVmPrograms)
|
|
152
172
|
{
|
|
153
173
|
EventLogQuery query = new EventLogQuery(log, PathType.LogName, xpath);
|
|
154
174
|
query.ReverseDirection = true;
|
|
@@ -163,37 +183,58 @@ static class Program
|
|
|
163
183
|
return;
|
|
164
184
|
}
|
|
165
185
|
DateTime at = record.TimeCreated.HasValue ? record.TimeCreated.Value.ToUniversalTime() : DateTime.UtcNow;
|
|
186
|
+
string detail = Detail(record);
|
|
187
|
+
if (onlyVmPrograms && !MentionsVmProgram(detail))
|
|
188
|
+
{
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
166
191
|
items.Add(
|
|
167
192
|
"{\"at\":" + Json(at.ToString("o", CultureInfo.InvariantCulture)) +
|
|
168
193
|
",\"id\":" + record.Id.ToString(CultureInfo.InvariantCulture) +
|
|
169
194
|
",\"provider\":" + Json(record.ProviderName ?? "") +
|
|
170
|
-
",\"process\":" + Json(
|
|
195
|
+
",\"process\":" + Json(detail) + "}");
|
|
171
196
|
}
|
|
172
197
|
}
|
|
173
198
|
}
|
|
174
199
|
}
|
|
175
200
|
|
|
176
|
-
/// <summary>
|
|
201
|
+
/// <summary>
|
|
202
|
+
/// What an event says beyond its id: the program that asked for a restart (1074), else its message start (the
|
|
203
|
+
/// resource-exhaustion event 2004 lists the biggest memory users, so it gets more room). Not for the plain power events.
|
|
204
|
+
/// </summary>
|
|
177
205
|
static string Detail(EventRecord record)
|
|
178
206
|
{
|
|
179
207
|
if (record.Id == 1074)
|
|
180
208
|
{
|
|
181
209
|
return record.Properties.Count > 0 ? Convert.ToString(record.Properties[0].Value, CultureInfo.InvariantCulture) ?? "" : "";
|
|
182
210
|
}
|
|
183
|
-
if (record.Id ==
|
|
211
|
+
if (record.Id == 41)
|
|
184
212
|
{
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
{
|
|
188
|
-
message = record.FormatDescription() ?? "";
|
|
189
|
-
}
|
|
190
|
-
catch (EventLogException)
|
|
191
|
-
{
|
|
192
|
-
message = "";
|
|
193
|
-
}
|
|
194
|
-
return message.Substring(0, Math.Min(MaxDetail, message.Length));
|
|
213
|
+
// Kernel-Power 41 carries the stop code of a blue screen (0 when there was none: a freeze or power loss).
|
|
214
|
+
return record.Properties.Count > 0 ? "BugcheckCode " + Convert.ToString(record.Properties[0].Value, CultureInfo.InvariantCulture) : "";
|
|
195
215
|
}
|
|
196
|
-
|
|
216
|
+
if (record.Id == 42 || record.Id == 6008)
|
|
217
|
+
{
|
|
218
|
+
return "";
|
|
219
|
+
}
|
|
220
|
+
string message;
|
|
221
|
+
try
|
|
222
|
+
{
|
|
223
|
+
message = record.FormatDescription() ?? "";
|
|
224
|
+
}
|
|
225
|
+
catch (EventLogException)
|
|
226
|
+
{
|
|
227
|
+
message = "";
|
|
228
|
+
}
|
|
229
|
+
int max = record.Id == 2004 ? MaxResourceDetail : MaxDetail;
|
|
230
|
+
return message.Substring(0, Math.Min(max, message.Length));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/// <summary>Whether an application crash report names a program that runs the sandbox's VM.</summary>
|
|
234
|
+
static bool MentionsVmProgram(string detail)
|
|
235
|
+
{
|
|
236
|
+
string lower = detail.ToLowerInvariant();
|
|
237
|
+
return lower.Contains("vmwp.exe") || lower.Contains("vmmem") || lower.Contains("vmcompute") || lower.Contains("windowssandbox");
|
|
197
238
|
}
|
|
198
239
|
|
|
199
240
|
/// <summary>A JSON string literal.</summary>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#Requires -Version 5.1
|
|
2
|
+
# Looks at the contained-storage image of one data set without changing anything and prints one line of JSON:
|
|
3
|
+
# { imageExists, imageBytes, mountExists, mountIsMountPoint, attached }. `attached` is null when Windows will not say
|
|
4
|
+
# without administrator rights. Used by doctor, repair and the launch (host/storage/contained) to compare what the
|
|
5
|
+
# state file says with what is really mounted. Paths are never followed through links: a link gives mountIsMountPoint.
|
|
6
|
+
param(
|
|
7
|
+
[Parameter(Mandatory = $true)][string]$Image,
|
|
8
|
+
[Parameter(Mandatory = $true)][string]$Mount
|
|
9
|
+
)
|
|
10
|
+
$ErrorActionPreference = 'Stop'
|
|
11
|
+
|
|
12
|
+
$answer = [ordered]@{ imageExists = $false; imageBytes = 0; mountExists = $false; mountIsMountPoint = $false; attached = $null }
|
|
13
|
+
try {
|
|
14
|
+
if (Test-Path -LiteralPath $Image -PathType Leaf) {
|
|
15
|
+
$answer.imageExists = $true
|
|
16
|
+
$answer.imageBytes = (Get-Item -LiteralPath $Image -Force).Length
|
|
17
|
+
try { $answer.attached = [bool](Get-DiskImage -ImagePath $Image -ErrorAction Stop).Attached } catch { $answer.attached = $null }
|
|
18
|
+
}
|
|
19
|
+
if (Test-Path -LiteralPath $Mount -PathType Container) {
|
|
20
|
+
$answer.mountExists = $true
|
|
21
|
+
$answer.mountIsMountPoint = [bool]([IO.File]::GetAttributes($Mount) -band [IO.FileAttributes]::ReparsePoint)
|
|
22
|
+
}
|
|
23
|
+
Write-Output ($answer | ConvertTo-Json -Compress)
|
|
24
|
+
exit 0
|
|
25
|
+
} catch {
|
|
26
|
+
[Console]::Error.WriteLine($_.Exception.Message)
|
|
27
|
+
exit 1
|
|
28
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
#Requires -Version 5.1
|
|
2
|
+
# Shared by mount-contained.ps1, dismount-contained.ps1 and check-contained.ps1 (dot-sourced; defines functions only).
|
|
3
|
+
# Everything a request says is checked again here, because mounting a disk image needs administrator rights and the
|
|
4
|
+
# request file was written by a program that is not elevated: the image and the mount folder must be exactly
|
|
5
|
+
# <storage>\images\<name>.vhdx and <storage>\mounts\<name>, none of the folders on the way may be a link, and what is
|
|
6
|
+
# there must be yours. Nothing in this file changes anything.
|
|
7
|
+
Set-StrictMode -Version 2.0
|
|
8
|
+
|
|
9
|
+
$script:NameRule = '^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$'
|
|
10
|
+
$script:PathRule = '^[^"<>|*?\x00-\x1f]+$'
|
|
11
|
+
$script:AdministratorsSid = 'S-1-5-32-544'
|
|
12
|
+
$script:Lines = New-Object System.Collections.Generic.List[string]
|
|
13
|
+
|
|
14
|
+
function Add-Note([string]$Text) {
|
|
15
|
+
$script:Lines.Add($Text)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function Test-PlainAbsolute([string]$Path) {
|
|
19
|
+
if ([string]::IsNullOrWhiteSpace($Path)) { return $false }
|
|
20
|
+
if ($Path -notmatch $script:PathRule) { return $false }
|
|
21
|
+
# A drive letter and plain folder names: no UNC or \\?\ form, no . or .. part, no stream (a colon), no slash.
|
|
22
|
+
# (GetFullPath is not used: it turns a short 8.3 name such as WDAGUT~1 into a long one, and those are fine.)
|
|
23
|
+
if ($Path -notmatch '^[A-Za-z]:\\') { return $false }
|
|
24
|
+
if ($Path.IndexOf(':', 2) -ge 0 -or $Path.Contains('/')) { return $false }
|
|
25
|
+
foreach ($part in $Path.Substring(3).TrimEnd('\').Split('\')) {
|
|
26
|
+
if ($part -eq '' -or $part -eq '.' -or $part -eq '..' -or $part.EndsWith(' ') -or $part.EndsWith('.')) { return $false }
|
|
27
|
+
}
|
|
28
|
+
return $true
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function Test-ReparsePoint([string]$Path) {
|
|
32
|
+
return [bool]([IO.File]::GetAttributes($Path) -band [IO.FileAttributes]::ReparsePoint)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function Test-IsMe([string]$Sid) {
|
|
36
|
+
$me = [Security.Principal.WindowsIdentity]::GetCurrent()
|
|
37
|
+
return ($me.User.Value -ieq $Sid)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function Test-IsAdministrator {
|
|
41
|
+
$me = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
|
|
42
|
+
return $me.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function Read-ContainedRequest([string]$JsonPath, [string]$Sha256, [bool]$Dry) {
|
|
46
|
+
if (-not (Test-Path -LiteralPath $JsonPath -PathType Leaf)) { throw "The request file is missing: $JsonPath" }
|
|
47
|
+
$bytes = [IO.File]::ReadAllBytes($JsonPath)
|
|
48
|
+
if ($bytes.Length -gt 65536) { throw 'The request file is too large.' }
|
|
49
|
+
if ($Sha256) {
|
|
50
|
+
$hash = [BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash($bytes)).Replace('-', '')
|
|
51
|
+
if ($hash -ine $Sha256) { throw 'The request file changed after it was written; nothing was done.' }
|
|
52
|
+
} elseif (-not $Dry) {
|
|
53
|
+
throw 'No checksum was given for the request file.'
|
|
54
|
+
}
|
|
55
|
+
$text = [Text.Encoding]::UTF8.GetString($bytes).TrimStart([char]0xFEFF)
|
|
56
|
+
return ($text | ConvertFrom-Json)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function Assert-ContainedRequest($Req) {
|
|
60
|
+
foreach ($name in @('storageRoot', 'image', 'mount', 'userSid', 'set')) {
|
|
61
|
+
if (-not ($Req.PSObject.Properties.Name -contains $name) -or -not $Req.$name) { throw "The request has no $name." }
|
|
62
|
+
}
|
|
63
|
+
if ($Req.userSid -notmatch '^S-1-[0-9-]{3,120}$') { throw 'The request has no valid user.' }
|
|
64
|
+
if ($Req.set -notmatch $script:NameRule) { throw "The data set name is not allowed: $($Req.set)" }
|
|
65
|
+
foreach ($p in @($Req.storageRoot, $Req.image, $Req.mount)) {
|
|
66
|
+
if (-not (Test-PlainAbsolute $p)) { throw "Not a plain full path (no .., no network path): $p" }
|
|
67
|
+
}
|
|
68
|
+
$root = $Req.storageRoot.TrimEnd('\')
|
|
69
|
+
$wantImage = Join-Path (Join-Path $root 'images') ($Req.set + '.vhdx')
|
|
70
|
+
$wantMount = Join-Path (Join-Path $root 'mounts') $Req.set
|
|
71
|
+
if ($Req.image.TrimEnd('\') -ine $wantImage) { throw "The image must be $wantImage, not $($Req.image)." }
|
|
72
|
+
if ($Req.mount.TrimEnd('\') -ine $wantMount) { throw "The mount folder must be $wantMount, not $($Req.mount)." }
|
|
73
|
+
if ($root.Length -le 3) { throw 'The storage folder cannot be a drive root.' }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function Assert-NoLinksOnTheWay($Req, [bool]$MountMayBeLink) {
|
|
77
|
+
$root = $Req.storageRoot.TrimEnd('\')
|
|
78
|
+
foreach ($p in @($root, (Join-Path $root 'images'), (Join-Path $root 'mounts'))) {
|
|
79
|
+
if (-not (Test-Path -LiteralPath $p -PathType Container)) { throw "A folder is missing: $p" }
|
|
80
|
+
if (Test-ReparsePoint $p) { throw "Refusing a link: $p" }
|
|
81
|
+
}
|
|
82
|
+
# Everything above the storage folder must not be a link either (a link there would move the whole tree).
|
|
83
|
+
$parent = Split-Path -Parent $root
|
|
84
|
+
while ($parent) {
|
|
85
|
+
if ((Test-Path -LiteralPath $parent) -and (Test-ReparsePoint $parent)) { throw "Refusing a link above the storage folder: $parent" }
|
|
86
|
+
$next = Split-Path -Parent $parent
|
|
87
|
+
if ($next -eq $parent) { break }
|
|
88
|
+
$parent = $next
|
|
89
|
+
}
|
|
90
|
+
if (Test-Path -LiteralPath $Req.image) {
|
|
91
|
+
if (Test-ReparsePoint $Req.image) { throw "Refusing a link: $($Req.image)" }
|
|
92
|
+
if (-not (Test-Path -LiteralPath $Req.image -PathType Leaf)) { throw "The image is not a file: $($Req.image)" }
|
|
93
|
+
}
|
|
94
|
+
if (-not (Test-Path -LiteralPath $Req.mount -PathType Container)) { throw "The mount folder is missing: $($Req.mount)" }
|
|
95
|
+
if ((-not $MountMayBeLink) -and (Test-ReparsePoint $Req.mount)) { throw "Refusing a link: $($Req.mount)" }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function Get-OwnerSid([string]$Path) {
|
|
99
|
+
return (Get-Acl -LiteralPath $Path).GetOwner([Security.Principal.SecurityIdentifier]).Value
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function Assert-OwnedByUser($Req, [string[]]$Paths) {
|
|
103
|
+
# An object made by an elevated process of your own account is owned by Administrators, not by you.
|
|
104
|
+
$allowed = @($Req.userSid)
|
|
105
|
+
if ((Test-IsMe $Req.userSid) -and (Test-IsAdministrator)) { $allowed += $script:AdministratorsSid }
|
|
106
|
+
foreach ($p in $Paths) {
|
|
107
|
+
$owner = Get-OwnerSid $p
|
|
108
|
+
if ($allowed -inotcontains $owner) { throw "Not yours (owner $owner): $p" }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function Read-ExactBytes([IO.FileStream]$Stream, [long]$Offset, [int]$Count) {
|
|
113
|
+
if ($Offset -lt 0 -or ($Offset + $Count) -gt $Stream.Length) { throw 'The image is not a valid VHDX file (a part is missing).' }
|
|
114
|
+
[void]$Stream.Seek($Offset, [IO.SeekOrigin]::Begin)
|
|
115
|
+
$buffer = New-Object byte[] $Count
|
|
116
|
+
$done = 0
|
|
117
|
+
while ($done -lt $Count) {
|
|
118
|
+
$n = $Stream.Read($buffer, $done, $Count - $done)
|
|
119
|
+
if ($n -le 0) { throw 'The image could not be read.' }
|
|
120
|
+
$done += $n
|
|
121
|
+
}
|
|
122
|
+
return , $buffer
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function New-GuidFrom([byte[]]$Bytes, [int]$Offset) {
|
|
126
|
+
$part = New-Object byte[] 16
|
|
127
|
+
[Array]::Copy($Bytes, $Offset, $part, 0, 16)
|
|
128
|
+
return (New-Object System.Guid -ArgumentList (, $part))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# Reads the VHDX header of a file that is not attached (the layout is in the VHDX specification): is it a VHDX, how
|
|
132
|
+
# big may it grow, does it have a parent (a differencing disk would reach into another file)?
|
|
133
|
+
function Read-VhdxInfo([string]$Path) {
|
|
134
|
+
$metadataRegion = New-Object System.Guid '8b7ca206-4790-4b9a-b8fe-575f050f886e'
|
|
135
|
+
$virtualSizeItem = New-Object System.Guid '2fa54224-cd1b-4876-b211-5dbed83bf4b8'
|
|
136
|
+
$parentItem = New-Object System.Guid 'a8d35f2d-b30b-454d-abf7-d3d84834ab0c'
|
|
137
|
+
$stream = [IO.File]::Open($Path, [IO.FileMode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::ReadWrite)
|
|
138
|
+
try {
|
|
139
|
+
$id = [Text.Encoding]::ASCII.GetString((Read-ExactBytes $stream 0 8))
|
|
140
|
+
if ($id -ne 'vhdxfile') { throw 'The file is not a VHDX image.' }
|
|
141
|
+
$table = Read-ExactBytes $stream 196608 65536
|
|
142
|
+
if ([Text.Encoding]::ASCII.GetString($table, 0, 4) -ne 'regi') { throw 'The image has no region table.' }
|
|
143
|
+
$count = [BitConverter]::ToUInt32($table, 8)
|
|
144
|
+
if ($count -gt 64) { throw 'The image has too many regions.' }
|
|
145
|
+
$metadataOffset = -1L
|
|
146
|
+
for ($i = 0; $i -lt $count; $i++) {
|
|
147
|
+
$at = 16 + 32 * $i
|
|
148
|
+
if ((New-GuidFrom $table $at) -eq $metadataRegion) { $metadataOffset = [long][BitConverter]::ToUInt64($table, $at + 16) }
|
|
149
|
+
}
|
|
150
|
+
if ($metadataOffset -lt 0) { throw 'The image has no metadata region.' }
|
|
151
|
+
$meta = Read-ExactBytes $stream $metadataOffset 65536
|
|
152
|
+
if ([Text.Encoding]::ASCII.GetString($meta, 0, 8) -ne 'metadata') { throw 'The image has no metadata table.' }
|
|
153
|
+
$items = [BitConverter]::ToUInt16($meta, 10)
|
|
154
|
+
if ($items -gt 2047) { throw 'The image has too many metadata items.' }
|
|
155
|
+
$size = [uint64]0
|
|
156
|
+
$hasParent = $false
|
|
157
|
+
for ($i = 0; $i -lt $items; $i++) {
|
|
158
|
+
$at = 32 + 32 * $i
|
|
159
|
+
$guid = New-GuidFrom $meta $at
|
|
160
|
+
if ($guid -eq $parentItem) { $hasParent = $true }
|
|
161
|
+
if ($guid -eq $virtualSizeItem) {
|
|
162
|
+
$itemOffset = [long][BitConverter]::ToUInt32($meta, $at + 16)
|
|
163
|
+
$size = [BitConverter]::ToUInt64((Read-ExactBytes $stream ($metadataOffset + $itemOffset) 8), 0)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return @{ VirtualSize = $size; HasParent = $hasParent }
|
|
167
|
+
} finally {
|
|
168
|
+
$stream.Dispose()
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function Assert-PlainImage($Req, [bool]$Dry) {
|
|
173
|
+
$info = Read-VhdxInfo $Req.image
|
|
174
|
+
if ($info.HasParent) { throw 'The image is a differencing disk (it has a parent file); refusing it.' }
|
|
175
|
+
$cap = [uint64]$Req.maxGb * 1GB
|
|
176
|
+
if ($info.VirtualSize -eq 0) { throw 'The image size could not be read.' }
|
|
177
|
+
if ($info.VirtualSize -gt $cap) { throw "The image is bigger than the limit of $($Req.maxGb) GB." }
|
|
178
|
+
return $info
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# Is this very image attached, and where? Null as the second answer when Windows will not say.
|
|
182
|
+
function Get-ImageState([string]$Image, [bool]$Dry) {
|
|
183
|
+
try {
|
|
184
|
+
$di = Get-DiskImage -ImagePath $Image -ErrorAction Stop
|
|
185
|
+
if (-not $di.Attached) { return @{ Attached = $false; Paths = @() } }
|
|
186
|
+
$disk = $di | Get-Disk -ErrorAction Stop
|
|
187
|
+
$paths = @()
|
|
188
|
+
foreach ($part in @(Get-Partition -DiskNumber $disk.Number -ErrorAction SilentlyContinue)) {
|
|
189
|
+
foreach ($a in @($part.AccessPaths)) { if ($a) { $paths += $a } }
|
|
190
|
+
}
|
|
191
|
+
return @{ Attached = $true; Paths = $paths }
|
|
192
|
+
} catch {
|
|
193
|
+
if ($Dry) { return @{ Attached = $false; Paths = @() } }
|
|
194
|
+
throw "Windows could not tell whether the image is attached: $($_.Exception.Message)"
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function Test-MountedAt($State, [string]$Mount) {
|
|
199
|
+
$want = $Mount.TrimEnd('\') + '\'
|
|
200
|
+
foreach ($p in $State.Paths) { if ($p -ieq $want) { return $true } }
|
|
201
|
+
return $false
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function Invoke-DiskPart([string[]]$DiskPartLines, [string]$TempDir, [bool]$Dry) {
|
|
205
|
+
foreach ($line in $DiskPartLines) {
|
|
206
|
+
if ($line -match '[\r\n]') { throw 'A diskpart line has a line break.' }
|
|
207
|
+
}
|
|
208
|
+
if ($Dry) {
|
|
209
|
+
foreach ($line in $DiskPartLines) { Add-Note ('DISKPART: ' + $line) }
|
|
210
|
+
return ''
|
|
211
|
+
}
|
|
212
|
+
$diskpart = Join-Path $env:SystemRoot 'System32\diskpart.exe'
|
|
213
|
+
if (-not (Test-Path -LiteralPath $diskpart)) { throw 'diskpart.exe was not found.' }
|
|
214
|
+
$file = Join-Path $TempDir ('diskpart-' + [guid]::NewGuid().ToString('N') + '.txt')
|
|
215
|
+
[IO.File]::WriteAllLines($file, $DiskPartLines, [Text.Encoding]::ASCII)
|
|
216
|
+
try {
|
|
217
|
+
# /s stops at the first command that fails.
|
|
218
|
+
$output = & $diskpart /s $file 2>&1 | Out-String
|
|
219
|
+
if ($LASTEXITCODE -ne 0) { throw "diskpart failed (exit $LASTEXITCODE): $output" }
|
|
220
|
+
return $output
|
|
221
|
+
} finally {
|
|
222
|
+
Remove-Item -LiteralPath $file -Force -ErrorAction SilentlyContinue
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function Write-ContainedResult([hashtable]$Result, [string]$JsonPath, [bool]$Dry) {
|
|
227
|
+
if ($Dry) {
|
|
228
|
+
foreach ($line in $script:Lines) { Write-Output $line }
|
|
229
|
+
if ($Result.ok) { Write-Output 'RESULT: ok' } else { Write-Output ('RESULT: refused: ' + $Result.message) }
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
$Result.notes = @($script:Lines)
|
|
233
|
+
$file = Join-Path (Split-Path -Parent $JsonPath) 'result.json'
|
|
234
|
+
[IO.File]::WriteAllText($file, ($Result | ConvertTo-Json -Compress), (New-Object Text.UTF8Encoding($false)))
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function Enter-ContainedLock {
|
|
238
|
+
$mutex = New-Object System.Threading.Mutex($false, 'Global\AgentSandboxContainedStorage')
|
|
239
|
+
if (-not $mutex.WaitOne(30000)) { throw 'Another mount or dismount is running. Try again in a minute.' }
|
|
240
|
+
return $mutex
|
|
241
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#Requires -Version 5.1
|
|
2
|
+
# Detaches the contained-storage disk image of one data set (the image file stays, with everything in it). Run elevated
|
|
3
|
+
# (one Windows permission prompt). The request is a JSON file: { set, storageRoot, image, mount, userSid }. The same
|
|
4
|
+
# checks as mount-contained.ps1 apply (exact paths, no links, yours). It refuses while Windows Sandbox is running (the
|
|
5
|
+
# sandbox may be using the folders in the image). Only `detach vdisk` for this one image is run; nothing is deleted.
|
|
6
|
+
# -DryRun prints the diskpart lines and does nothing else; the result goes to result.json next to the request file.
|
|
7
|
+
param(
|
|
8
|
+
[Parameter(Mandatory = $true)][string]$Json,
|
|
9
|
+
[string]$Sha256 = '',
|
|
10
|
+
[switch]$DryRun
|
|
11
|
+
)
|
|
12
|
+
$ErrorActionPreference = 'Stop'
|
|
13
|
+
. (Join-Path $PSScriptRoot 'contained-common.ps1')
|
|
14
|
+
|
|
15
|
+
$result = @{ ok = $false; message = ''; action = 'dismount'; created = $false; alreadyMounted = $false }
|
|
16
|
+
|
|
17
|
+
function Invoke-Dismount($Req, [bool]$Dry, [string]$TempDir) {
|
|
18
|
+
Assert-NoLinksOnTheWay $Req $true
|
|
19
|
+
$root = $Req.storageRoot.TrimEnd('\')
|
|
20
|
+
Assert-OwnedByUser $Req @($root, (Join-Path $root 'images'), (Join-Path $root 'mounts'))
|
|
21
|
+
if (-not (Test-Path -LiteralPath $Req.image -PathType Leaf)) { throw "The image is missing: $($Req.image)" }
|
|
22
|
+
Assert-OwnedByUser $Req @($Req.image)
|
|
23
|
+
$image = $Req.image
|
|
24
|
+
$mount = $Req.mount.TrimEnd('\')
|
|
25
|
+
if (-not $Dry) {
|
|
26
|
+
if (Get-Process -Name 'WindowsSandbox*' -ErrorAction SilentlyContinue) {
|
|
27
|
+
throw 'A Windows Sandbox is running and may be using the image. Close it first.'
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
$state = Get-ImageState $image $Dry
|
|
31
|
+
if ((-not $Dry) -and (-not $state.Attached)) {
|
|
32
|
+
if (Test-ReparsePoint $mount) { throw "The mount folder is a mount point of something else; not touching it: $mount" }
|
|
33
|
+
$script:result.ok = $true
|
|
34
|
+
$script:result.message = 'The image is not mounted.'
|
|
35
|
+
Add-Note 'not attached: nothing to do'
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
[void](Invoke-DiskPart @("select vdisk file=""$image""", 'detach vdisk') $TempDir $Dry)
|
|
39
|
+
if (-not $Dry) {
|
|
40
|
+
$after = Get-ImageState $image $false
|
|
41
|
+
if ($after.Attached) { throw 'The image is still attached (something is using it). Close programs that use files in it and try again.' }
|
|
42
|
+
if (Test-ReparsePoint $mount) { throw "The mount folder is still a mount point: $mount" }
|
|
43
|
+
}
|
|
44
|
+
$script:result.ok = $true
|
|
45
|
+
$script:result.message = 'The image was detached.'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
$mutex = $null
|
|
49
|
+
try {
|
|
50
|
+
$request = Read-ContainedRequest -JsonPath $Json -Sha256 $Sha256 -Dry $DryRun.IsPresent
|
|
51
|
+
Assert-ContainedRequest $request
|
|
52
|
+
if (-not $DryRun) {
|
|
53
|
+
if (-not (Test-IsAdministrator)) { throw 'Detaching a disk image needs administrator rights.' }
|
|
54
|
+
$mutex = Enter-ContainedLock
|
|
55
|
+
}
|
|
56
|
+
Invoke-Dismount $request $DryRun.IsPresent (Split-Path -Parent $Json)
|
|
57
|
+
} catch {
|
|
58
|
+
$result.ok = $false
|
|
59
|
+
$result.message = $_.Exception.Message
|
|
60
|
+
} finally {
|
|
61
|
+
if ($mutex) { $mutex.ReleaseMutex(); $mutex.Dispose() }
|
|
62
|
+
}
|
|
63
|
+
Write-ContainedResult $result $Json $DryRun.IsPresent
|
|
64
|
+
if ($result.ok) { exit 0 }
|
|
65
|
+
if ($DryRun) { [Console]::Error.WriteLine($result.message) }
|
|
66
|
+
exit 1
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#Requires -Version 5.1
|
|
2
|
+
# Prints this PC's memory as JSON: { totalMb, availableMb, commitLimitMb, commitAvailableMb }. Read-only. The fallback
|
|
3
|
+
# for HostTools.exe memory-status (host/life/host-memory.ts).
|
|
4
|
+
param()
|
|
5
|
+
$ErrorActionPreference = 'Stop'
|
|
6
|
+
|
|
7
|
+
$os = Get-CimInstance -ClassName Win32_OperatingSystem
|
|
8
|
+
# The paging file setup from the registry: auto (Windows manages it), fixed (with its maximum sizes) or none.
|
|
9
|
+
$pageFile = 'auto'
|
|
10
|
+
$pageFileMax = 0
|
|
11
|
+
try {
|
|
12
|
+
$entries = @((Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management' -ErrorAction Stop).PagingFiles | Where-Object { $_ -and $_.Trim() })
|
|
13
|
+
$pageFile = 'none'
|
|
14
|
+
foreach ($entry in $entries) {
|
|
15
|
+
$parts = $entry.Trim() -split ' '
|
|
16
|
+
$initial = 0; $max = 0
|
|
17
|
+
if ($parts.Count -ge 3) { $initial = [int64]$parts[-2]; $max = [int64]$parts[-1] }
|
|
18
|
+
if ($entry.StartsWith('?:') -or ($initial -eq 0 -and $max -eq 0)) { $pageFile = 'auto'; $pageFileMax = 0; break }
|
|
19
|
+
$pageFile = 'fixed'
|
|
20
|
+
$pageFileMax += $max
|
|
21
|
+
}
|
|
22
|
+
} catch {
|
|
23
|
+
# Not readable: treated as automatic (no warning).
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# Win32_OperatingSystem counts in KB.
|
|
27
|
+
ConvertTo-Json -Compress -InputObject ([ordered]@{
|
|
28
|
+
totalMb = [int64][math]::Floor($os.TotalVisibleMemorySize / 1024)
|
|
29
|
+
availableMb = [int64][math]::Floor($os.FreePhysicalMemory / 1024)
|
|
30
|
+
commitLimitMb = [int64][math]::Floor($os.TotalVirtualMemorySize / 1024)
|
|
31
|
+
commitAvailableMb = [int64][math]::Floor($os.FreeVirtualMemory / 1024)
|
|
32
|
+
pageFile = $pageFile
|
|
33
|
+
pageFileMaxMb = [int64]$pageFileMax
|
|
34
|
+
})
|